From c3d52105753dafdf2d993e540cc3192f23447dac Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Jun 2011 11:14:16 -0600 Subject: ALSA: hda: Gate ELD usage only by whether ELD is valid It's perfectly valid for an ELD to contain no SADs. This simply means that only basic audio is supoprted. In this case, we still want to limit a PCM's capabilities based on the ELD. History: * Originally, ELD application was limited solely by sad_count>0, which was used to check that an ELD had been read. * Later, eld_valid was added to the conditions to satisfy. This change removes the original sad_count>0 check, which when squashed with the above two changes ends up replacing if (sad_count) with if (eld_valid). Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bd0ae697f9c4..8ccec72a8f0c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -816,7 +816,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, *codec_pars = *hinfo; eld = &spec->sink_eld[idx]; - if (!static_hdmi_pcm && eld->eld_valid && eld->sad_count > 0) { + if (!static_hdmi_pcm && eld->eld_valid) { hdmi_eld_update_pcm_info(eld, hinfo, codec_pars); if (hinfo->channels_min > hinfo->channels_max || !hinfo->rates || !hinfo->formats) -- cgit v1.2.3 From 7c9359762797ba7a70bbaa6364aaecc16786ac83 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Jun 2011 11:14:17 -0600 Subject: ALSA: hda: Allow multple SPDIF controls per codec Currently, the data that backs the kcontrols created by snd_hda_create_spdif_out_ctls is stored directly in struct hda_codec. When multiple sets of these controls are stored, they will all manipulate the same data, causing confusion. Instead, store an array of this data, one copy per converter, to isolate the controls. This patch would cause a behavioural change in the case where snd_hda_create_spdif_out_ctls was called multiple times for a single codec. As best I can tell, this is never the case for any codec. This will be relevant at least for some HDMI audio codecs, such as the NVIDIA GeForce 520 and Intel Ibex Peak. A future change will modify the driver's handling of those codecs to create multiple PCMs per codec. Note that this issue isn't affected by whether one creates a PCM-per-converter or PCM-per-pin; there are multiple of both within a single codec in both of those codecs. Note that those codecs don't currently create multiple PCMs for the codec due to the default HW mux state of all pins being to point at the same converter, hence there is only a single converter routed to any pin, and hence only a single PCM. Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 80 +++++++++++++++++++++++++++++++--------------- sound/pci/hda/hda_codec.h | 12 +++++-- sound/pci/hda/hda_intel.c | 5 ++- sound/pci/hda/patch_hdmi.c | 19 ++++++----- sound/pci/hda/patch_via.c | 12 ++++--- 5 files changed, 87 insertions(+), 41 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 45b4a8d70e08..e17e2998d333 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1083,6 +1083,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->conn_lists); + snd_array_free(&codec->spdif_out); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1144,6 +1145,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); + snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); if (codec->bus->modelname) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { @@ -2555,11 +2557,13 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff; - ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff; + ucontrol->value.iec958.status[0] = spdif->status & 0xff; + ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; return 0; } @@ -2644,19 +2648,21 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + hda_nid_t nid = spdif->nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); - codec->spdif_status = ucontrol->value.iec958.status[0] | + spdif->status = ucontrol->value.iec958.status[0] | ((unsigned int)ucontrol->value.iec958.status[1] << 8) | ((unsigned int)ucontrol->value.iec958.status[2] << 16) | ((unsigned int)ucontrol->value.iec958.status[3] << 24); - val = convert_from_spdif_status(codec->spdif_status); - val |= codec->spdif_ctls & 1; - change = codec->spdif_ctls != val; - codec->spdif_ctls = val; + val = convert_from_spdif_status(spdif->status); + val |= spdif->ctls & 1; + change = spdif->ctls != val; + spdif->ctls = val; if (change) set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); @@ -2671,8 +2677,10 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE; + ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; return 0; } @@ -2680,17 +2688,19 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + hda_nid_t nid = spdif->nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); - val = codec->spdif_ctls & ~AC_DIG1_ENABLE; + val = spdif->ctls & ~AC_DIG1_ENABLE; if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; - change = codec->spdif_ctls != val; + change = spdif->ctls != val; if (change) { - codec->spdif_ctls = val; + spdif->ctls = val; set_dig_out_convert(codec, nid, val & 0xff, -1); /* unmute amp switch (if any) */ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && @@ -2750,30 +2760,46 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; int idx; + struct hda_spdif_out *spdif; idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch"); if (idx < 0) { printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); return -EBUSY; } + spdif = snd_array_new(&codec->spdif_out); for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); if (!kctl) return -ENOMEM; kctl->id.index = idx; - kctl->private_value = nid; + kctl->private_value = codec->spdif_out.used - 1; err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } - codec->spdif_ctls = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); + spdif->nid = nid; + spdif->ctls = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); +struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, + hda_nid_t nid) +{ + int i; + for (i = 0; i < codec->spdif_out.used; i++) { + struct hda_spdif_out *spdif = + snd_array_elem(&codec->spdif_out, i); + if (spdif->nid == nid) + return spdif; + } + return NULL; +} +EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); + /* * SPDIF sharing with analog output */ @@ -4177,10 +4203,12 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) { + struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(codec, nid); + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) set_dig_out_convert(codec, nid, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff, + spdif->ctls & ~AC_DIG1_ENABLE & 0xff, -1); snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); if (codec->slave_dig_outs) { @@ -4190,9 +4218,9 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, format); } /* turn on again (if needed) */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) set_dig_out_convert(codec, nid, - codec->spdif_ctls & 0xff, -1); + spdif->ctls & 0xff, -1); } static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) @@ -4348,6 +4376,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, { const hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); int i; mutex_lock(&codec->spdif_mutex); @@ -4356,7 +4386,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, if (chs == 2 && snd_hda_is_supported_format(codec, mout->dig_out_nid, format) && - !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { + !(spdif->status & IEC958_AES0_NONAUDIO)) { mout->dig_out_used = HDA_DIG_ANALOG_DUP; setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 59c97306c1de..1d21c0624e03 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -829,8 +829,7 @@ struct hda_codec { struct mutex spdif_mutex; struct mutex control_mutex; - unsigned int spdif_status; /* IEC958 status bits */ - unsigned short spdif_ctls; /* SPDIF control bits */ + struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ struct snd_array init_pins; /* initial (BIOS) pin configurations */ @@ -947,6 +946,15 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, hda_nid_t nid, unsigned int cfg); /* for hwdep */ void snd_hda_shutup_pins(struct hda_codec *codec); +/* SPDIF controls */ +struct hda_spdif_out { + hda_nid_t nid; /* Converter nid values relate to */ + unsigned int status; /* IEC958 status bits */ + unsigned short ctls; /* SPDIF control bits */ +}; +struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, + hda_nid_t nid); + /* * Mixer */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 486f6deb3eee..966f40147bc3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1706,13 +1706,16 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; unsigned int bufsize, period_bytes, format_val, stream_tag; int err; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); + unsigned short ctls = spdif ? spdif->ctls : 0; azx_stream_reset(chip, azx_dev); format_val = snd_hda_calc_stream_format(runtime->rate, runtime->channels, runtime->format, hinfo->maxbps, - apcm->codec->spdif_ctls); + ctls); if (!format_val) { snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n", diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 8ccec72a8f0c..86b35a071a83 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1352,6 +1352,9 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, int chs; unsigned int dataDCC1, dataDCC2, channel_id; int i; + struct hdmi_spec *spec = codec->spec; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(codec, spec->cvt[0]); mutex_lock(&codec->spdif_mutex); @@ -1361,12 +1364,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, dataDCC2 = 0x2; /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); /* set the stream id */ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, @@ -1378,12 +1381,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, /* turn on again (if needed) */ /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); + spdif->ctls & 0xff); snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, @@ -1400,12 +1403,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, *otherwise the IEC958 bits won't be updated */ if (codec->spdif_status_reset && - (codec->spdif_ctls & AC_DIG1_ENABLE)) + (spdif->ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); /* set the stream id */ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], @@ -1421,12 +1424,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, /* turn on again (if needed) */ /* enable and set the channel status audio/data flag */ if (codec->spdif_status_reset && - (codec->spdif_ctls & AC_DIG1_ENABLE)) { + (spdif->ctls & AC_DIG1_ENABLE)) { snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); + spdif->ctls & 0xff); snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 605c99e1e520..8304c748dfb7 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1236,28 +1236,30 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec, const hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; int i; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); mutex_lock(&codec->spdif_mutex); if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (chs == 2 && snd_hda_is_supported_format(codec, mout->dig_out_nid, format) && - !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { + !(spdif->status & IEC958_AES0_NONAUDIO)) { mout->dig_out_used = HDA_DIG_ANALOG_DUP; /* turn off SPDIF once; otherwise the IEC958 bits won't * be updated */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) + if (spdif->ctls & AC_DIG1_ENABLE) snd_hda_codec_write(codec, mout->dig_out_nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); snd_hda_codec_setup_stream(codec, mout->dig_out_nid, stream_tag, 0, format); /* turn on again (if needed) */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) + if (spdif->ctls & AC_DIG1_ENABLE) snd_hda_codec_write(codec, mout->dig_out_nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); + spdif->ctls & 0xff); } else { mout->dig_out_used = 0; snd_hda_codec_setup_stream(codec, mout->dig_out_nid, -- cgit v1.2.3 From 74b654c957e901e7596ebc7b9f5a1bea62b20509 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Jun 2011 11:14:18 -0600 Subject: ALSA: hda: Virtualize SPDIF out controls The SPDIF output controls apply to converter widgets. A future change will create a PCM device per pin widget, and hence a set of SPDIF output controls per pin widget, for certain HDMI codecs. To support this, we need the ability to virtualize the SPDIF output controls. Specifically: * Controls can be "unassigned" from real hardware when a converter is not used for the PCM the control was created for. * Control puts only write to hardware when they are assigned. * Controls can be "assigned" to real hardware when a converter is picked to support output for a particular PCM. * When a converter is assigned, the hardware is updated to the cached configuration. Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 62 +++++++++++++++++++++++++++++++----------- sound/pci/hda/hda_codec.h | 2 ++ sound/pci/hda/hda_local.h | 4 ++- sound/pci/hda/patch_analog.c | 4 ++- sound/pci/hda/patch_ca0110.c | 3 +- sound/pci/hda/patch_cirrus.c | 3 +- sound/pci/hda/patch_cmedia.c | 4 ++- sound/pci/hda/patch_conexant.c | 1 + sound/pci/hda/patch_hdmi.c | 3 +- sound/pci/hda/patch_realtek.c | 1 + sound/pci/hda/patch_sigmatel.c | 4 ++- sound/pci/hda/patch_via.c | 1 + 12 files changed, 69 insertions(+), 23 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e17e2998d333..c63a06703de3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2663,10 +2663,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, val |= spdif->ctls & 1; change = spdif->ctls != val; spdif->ctls = val; - - if (change) + if (change && nid != (u16)-1) set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); - mutex_unlock(&codec->spdif_mutex); return change; } @@ -2684,6 +2682,17 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, return 0; } +static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid, + int dig1, int dig2) +{ + set_dig_out_convert(codec, nid, dig1, dig2); + /* unmute amp switch (if any) */ + if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && + (dig1 & AC_DIG1_ENABLE)) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); +} + static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2699,15 +2708,9 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; change = spdif->ctls != val; - if (change) { - spdif->ctls = val; - set_dig_out_convert(codec, nid, val & 0xff, -1); - /* unmute amp switch (if any) */ - if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && - (val & AC_DIG1_ENABLE)) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - } + spdif->ctls = val; + if (change && nid != (u16)-1) + set_spdif_ctls(codec, nid, val & 0xff, -1); mutex_unlock(&codec->spdif_mutex); return change; } @@ -2754,7 +2757,9 @@ static struct snd_kcontrol_new dig_mixes[] = { * * Returns 0 if successful, or a negative error code. */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, + hda_nid_t associated_nid, + hda_nid_t cvt_nid) { int err; struct snd_kcontrol *kctl; @@ -2774,12 +2779,12 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) return -ENOMEM; kctl->id.index = idx; kctl->private_value = codec->spdif_out.used - 1; - err = snd_hda_ctl_add(codec, nid, kctl); + err = snd_hda_ctl_add(codec, associated_nid, kctl); if (err < 0) return err; } - spdif->nid = nid; - spdif->ctls = snd_hda_codec_read(codec, nid, 0, + spdif->nid = cvt_nid; + spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); spdif->status = convert_to_spdif_status(spdif->ctls); return 0; @@ -2800,6 +2805,31 @@ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); +void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) +{ + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + + mutex_lock(&codec->spdif_mutex); + spdif->nid = (u16)-1; + mutex_unlock(&codec->spdif_mutex); +} +EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign); + +void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) +{ + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + unsigned short val; + + mutex_lock(&codec->spdif_mutex); + if (spdif->nid != nid) { + spdif->nid = nid; + val = spdif->ctls; + set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff); + } + mutex_unlock(&codec->spdif_mutex); +} +EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign); + /* * SPDIF sharing with analog output */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1d21c0624e03..96c35cab57bf 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -954,6 +954,8 @@ struct hda_spdif_out { }; struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, hda_nid_t nid); +void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx); +void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); /* * Mixer diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 08ec073444e2..8b88c92826a1 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -212,7 +212,9 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, /* * SPDIF I/O */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid); +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, + hda_nid_t associated_nid, + hda_nid_t cvt_nid); int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); /* diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d694e9d4921d..0f7b8951440f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -213,7 +213,9 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 61b92634b161..6b406840846e 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -240,7 +240,8 @@ static int ca0110_build_controls(struct hda_codec *codec) } if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 26a1521045bb..c7b5ca28fa77 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -821,7 +821,8 @@ static int build_digital_output(struct hda_codec *codec) if (!spec->multiout.dig_out_nid) return 0; - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index ab3308daa960..9eaf99b01aec 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -327,7 +327,9 @@ static int cmi9880_build_controls(struct hda_codec *codec) return err; } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 3e6b9a8539c2..217ca9e13425 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -510,6 +510,7 @@ static int conexant_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, spec->multiout.dig_out_nid); if (err < 0) return err; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 86b35a071a83..13ee4449718f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1095,7 +1095,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) int i; for (i = 0; i < codec->num_pcms; i++) { - err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i], + spec->cvt[i]); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a4e10002f56..5c8a4ea75cd7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3217,6 +3217,7 @@ static int alc_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, spec->multiout.dig_out_nid); if (err < 0) return err; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 7f81cc2274f3..7407095cbc78 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1112,7 +1112,9 @@ static int stac92xx_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 8304c748dfb7..89a0f2a3d269 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1497,6 +1497,7 @@ static int via_build_controls(struct hda_codec *codec) if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, spec->multiout.dig_out_nid); if (err < 0) return err; -- cgit v1.2.3 From 3aaf898025b1f75f30457e00e890c9f7c43567ab Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Jun 2011 11:14:19 -0600 Subject: ALSA: hda: Separate generic and non-generic implementations A future change will significantly rework the generic implementation in order to support codecs with a different number of pins and converters. Isolate the more custom codec variants from this change by duplicating the small portions of generic code they share. This simplifies the later rework of that previously shared code, since we don't have to consider the more custom codecs, and also prevents support for those codecs from regressing. Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 75 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 13ee4449718f..92fb105da1e0 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1163,6 +1163,63 @@ static int patch_generic_hdmi(struct hda_codec *codec) return 0; } +/* + * Shared non-generic implementations + */ + +static int simple_playback_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + int i; + + codec->num_pcms = spec->num_cvts; + codec->pcm_info = info; + + for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + struct hda_pcm_stream *pstr; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); + + info->name = generic_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; + snd_BUG_ON(!spec->pcm_playback); + *pstr = *spec->pcm_playback; + pstr->nid = spec->cvt[i]; + if (pstr->channels_max <= 2 && chans && chans <= 16) + pstr->channels_max = chans; + } + + return 0; +} + +static int simple_playback_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err; + int i; + + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->cvt[i], + spec->cvt[i]); + if (err < 0) + return err; + } + + return 0; +} + +static void simple_playback_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + kfree(spec); +} + /* * Nvidia specific implementations */ @@ -1475,17 +1532,17 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = { }; static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { - .build_controls = generic_hdmi_build_controls, - .build_pcms = generic_hdmi_build_pcms, + .build_controls = simple_playback_build_controls, + .build_pcms = simple_playback_build_pcms, .init = nvhdmi_7x_init, - .free = generic_hdmi_free, + .free = simple_playback_free, }; static const struct hda_codec_ops nvhdmi_patch_ops_2ch = { - .build_controls = generic_hdmi_build_controls, - .build_pcms = generic_hdmi_build_pcms, + .build_controls = simple_playback_build_controls, + .build_pcms = simple_playback_build_pcms, .init = nvhdmi_7x_init, - .free = generic_hdmi_free, + .free = simple_playback_free, }; static int patch_nvhdmi_2ch(struct hda_codec *codec) @@ -1596,10 +1653,10 @@ static int atihdmi_init(struct hda_codec *codec) } static const struct hda_codec_ops atihdmi_patch_ops = { - .build_controls = generic_hdmi_build_controls, - .build_pcms = generic_hdmi_build_pcms, + .build_controls = simple_playback_build_controls, + .build_pcms = simple_playback_build_pcms, .init = atihdmi_init, - .free = generic_hdmi_free, + .free = simple_playback_free, }; -- cgit v1.2.3 From 2def8172c6611f2577260287ebf5dd3b63f7ef55 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Jun 2011 11:14:20 -0600 Subject: ALSA: hda: hdmi_eld_update_pcm_info: update a stream in place A future change won't store an entire hda_pcm_stream just to represent the capabilities of a codec; a custom data-structure will be used. To ease that transition, modify hdmi_eld_update_pcm_info to expect the hda_pcm_stream to be pre-initialized with the codec's capabilities, and to update those capabilities in-place based on the ELD. Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_eld.c | 46 ++++++++++++++++++++++++---------------------- sound/pci/hda/hda_local.h | 4 ++-- sound/pci/hda/patch_hdmi.c | 18 ++++++++++-------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index b05f7be9dc1b..473cfa13a30d 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -580,43 +580,45 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) #endif /* CONFIG_PROC_FS */ /* update PCM info based on ELD */ -void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm, - struct hda_pcm_stream *codec_pars) +void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, + struct hda_pcm_stream *hinfo) { + u32 rates; + u64 formats; + unsigned int maxbps; + unsigned int channels_max; int i; /* assume basic audio support (the basic audio flag is not in ELD; * however, all audio capable sinks are required to support basic * audio) */ - pcm->rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; - pcm->formats = SNDRV_PCM_FMTBIT_S16_LE; - pcm->maxbps = 16; - pcm->channels_max = 2; + rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000; + formats = SNDRV_PCM_FMTBIT_S16_LE; + maxbps = 16; + channels_max = 2; for (i = 0; i < eld->sad_count; i++) { struct cea_sad *a = &eld->sad[i]; - pcm->rates |= a->rates; - if (a->channels > pcm->channels_max) - pcm->channels_max = a->channels; + rates |= a->rates; + if (a->channels > channels_max) + channels_max = a->channels; if (a->format == AUDIO_CODING_TYPE_LPCM) { if (a->sample_bits & AC_SUPPCM_BITS_20) { - pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (pcm->maxbps < 20) - pcm->maxbps = 20; + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (maxbps < 20) + maxbps = 20; } if (a->sample_bits & AC_SUPPCM_BITS_24) { - pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (pcm->maxbps < 24) - pcm->maxbps = 24; + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (maxbps < 24) + maxbps = 24; } } } - if (!codec_pars) - return; - /* restrict the parameters by the values the codec provides */ - pcm->rates &= codec_pars->rates; - pcm->formats &= codec_pars->formats; - pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max); - pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps); + hinfo->rates &= rates; + hinfo->formats &= formats; + hinfo->maxbps = min(hinfo->maxbps, maxbps); + hinfo->channels_max = min(hinfo->channels_max, channels_max); } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8b88c92826a1..b333bf46a19c 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -641,8 +641,8 @@ struct hdmi_eld { int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); void snd_hdmi_show_eld(struct hdmi_eld *eld); -void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm, - struct hda_pcm_stream *codec_pars); +void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, + struct hda_pcm_stream *hinfo); #ifdef CONFIG_PROC_FS int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 92fb105da1e0..338546531c17 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -815,20 +815,22 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (!codec_pars->rates) *codec_pars = *hinfo; + /* Initially set the converter's capabilities */ + hinfo->channels_min = codec_pars->channels_min; + hinfo->channels_max = codec_pars->channels_max; + hinfo->rates = codec_pars->rates; + hinfo->formats = codec_pars->formats; + hinfo->maxbps = codec_pars->maxbps; + eld = &spec->sink_eld[idx]; if (!static_hdmi_pcm && eld->eld_valid) { - hdmi_eld_update_pcm_info(eld, hinfo, codec_pars); + snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || !hinfo->rates || !hinfo->formats) return -ENODEV; - } else { - /* fallback to the codec default */ - hinfo->channels_max = codec_pars->channels_max; - hinfo->rates = codec_pars->rates; - hinfo->formats = codec_pars->formats; - hinfo->maxbps = codec_pars->maxbps; } - /* store the updated parameters */ + + /* Store the updated parameters */ runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; runtime->hw.formats = hinfo->formats; -- cgit v1.2.3 From 384a48d71520ca569a63f1e61e51a538bedb16df Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Jun 2011 11:14:21 -0600 Subject: ALSA: hda: HDMI: Support codecs with fewer cvts than pins The general concept of this change is to create a PCM device for each pin widget instead of each converter widget. Whenever a PCM is opened, a converter is dynamically selected to drive that pin based on those available for muxing into the pin. The one thing this model doesn't support is a single PCM/converter sending audio to multiple pin widgets at once. Note that this means that a struct hda_pcm_stream's nid variable is set to 0 except between a stream's open and cleanup calls. The dynamic de-assignment of converters to PCMs occurs within cleanup, not close, in order for it to co-incide with when controller stream IDs are cleaned up from converters. While the PCM for a pin is not open, the pin is disabled (its widget control's PIN_OUT bit is cleared) so that if the currently routed converter is used to drive a different PCM/pin, that audio does not leak out over a disabled pin. We use the recently added SPDIF virtualization feature in order to create SPDIF controls for each pin widget instead of each converter widget, so that state is specific to a PCM. In order to support this, a number of more mechanical changes are made: * s/nid/pin_nid/ or s/nid/cvt_nid/ in many places in order to make it clear exactly what the code is dealing with. * We now have per_pin and per_cvt arrays in hdmi_spec to store relevant data. In particular, we store a converter's capabilities in the per_cvt entry, rather than relying on a combination of codec_pcm_pars and the struct hda_pcm_stream. * ELD-related workarounds were removed from hdmi_channel_allocation into hdmi_instrinsic in order to simplifiy infoframe calculations and remove HW dependencies. * Various functions only apply to a single pin, since there is now only 1 pin per PCM. For example, hdmi_setup_infoframe, hdmi_setup_stream. * hdmi_add_pin and hdmi_add_cvt are more oriented at pure codec parsing and data retrieval, rather than determining which pins/converters are to be used for creating PCMs. This is quite a large change; it may be appropriate to simply read the result of the patch rather than the diffs. Some small parts of the change might be separable into different patches, but I think the bulk of the change will probably always be one large patch. Hopefully the change isn't too opaque! This has been tested on: * NVIDIA GeForce 400 series discrete graphics card. This model has the classical 1:1:1 codec:converter:pcm widget model. Tested stereo PCM audio to a PC monitor that supports audio. * NVIDIA GeForce 520 discrete graphics card. This model is the new 1 codec n converters m pins m>n model. Tested stereo PCM audio to a PC monitor that supports audio. * NVIDIA GeForce 400 series laptop graphics chip. This model has the classical 1:1:1 codec:converter:pcm widget model. Tested stereo PCM, multi-channel PCM, and AC3 pass-through to an AV receiver. * Intel Ibex Peak laptop. This model is the new 1 codec n converters m pins m>n model. Tested stereo PCM, multi-channel PCM, and AC3 pass- through to an AV receiver. Note that I'm not familiar at all with AC3 pass-through. Hence, I may not have covered all possible mechanisms that are applicable here. I do know that my receiver definitely received AC3, not decoded PCM. I tested with mplayer's "-afm hwac3" and/or "-af lavcac3enc" options, and alsa a WAV file that I believe has AC3 content rather than PCM. I also tested: * Play a stream * Mute while playing * Stop stream * Play some other streams to re-assign the converter to a different pin, PCM, set of SPDIF controls, ... hence hopefully triggering cleanup for the original PCM. * Unmute original stream while not playing * Play a stream on the original pin/PCM. This was to test SPDIF control virtualization. Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 +- sound/pci/hda/hda_codec.h | 2 + sound/pci/hda/patch_hdmi.c | 611 +++++++++++++++++++++++++-------------------- 3 files changed, 350 insertions(+), 266 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c63a06703de3..ce418c805a1a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3412,7 +3412,7 @@ static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) * * Returns 0 if successful, otherwise a negative error code. */ -static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, +int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp) { unsigned int i, val, wcaps; @@ -3504,6 +3504,7 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, return 0; } +EXPORT_SYMBOL_HDA(snd_hda_query_supported_pcm); /** * snd_hda_is_supported_format - Check the validity of the format diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 96c35cab57bf..070efac7e207 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -903,6 +903,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp); struct hda_verb { hda_nid_t nid; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 338546531c17..19cb72db9c38 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -43,7 +43,7 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); /* * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device - * could support two independent pipes, each of them can be connected to one or + * could support N independent pipes, each of them can be connected to one or * more ports (DVI, HDMI or DisplayPort). * * The HDA correspondence of pipes/ports are converter/pin nodes. @@ -51,30 +51,33 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); #define MAX_HDMI_CVTS 4 #define MAX_HDMI_PINS 4 -struct hdmi_spec { - int num_cvts; - int num_pins; - hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */ - hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */ +struct hdmi_spec_per_cvt { + hda_nid_t cvt_nid; + int assigned; + unsigned int channels_min; + unsigned int channels_max; + u32 rates; + u64 formats; + unsigned int maxbps; +}; - /* - * source connection for each pin - */ - hda_nid_t pin_cvt[MAX_HDMI_PINS+1]; +struct hdmi_spec_per_pin { + hda_nid_t pin_nid; + int num_mux_nids; + hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + struct hdmi_eld sink_eld; +}; - /* - * HDMI sink attached to each pin - */ - struct hdmi_eld sink_eld[MAX_HDMI_PINS]; +struct hdmi_spec { + int num_cvts; + struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS]; - /* - * export one pcm per pipe - */ - struct hda_pcm pcm_rec[MAX_HDMI_CVTS]; - struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS]; + int num_pins; + struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; + struct hda_pcm pcm_rec[MAX_HDMI_PINS]; /* - * ati/nvhdmi specific + * Non-generic ATI/NVIDIA specific */ struct hda_multi_out multiout; const struct hda_pcm_stream *pcm_playback; @@ -284,15 +287,40 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { * HDMI routines */ -static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid) { - int i; + int pin_idx; - for (i = 0; nids[i]; i++) - if (nids[i] == nid) - return i; + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) + if (spec->pins[pin_idx].pin_nid == pin_nid) + return pin_idx; - snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid); + return -EINVAL; +} + +static int hinfo_to_pin_index(struct hdmi_spec *spec, + struct hda_pcm_stream *hinfo) +{ + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) + if (&spec->pcm_rec[pin_idx].stream[0] == hinfo) + return pin_idx; + + snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo); + return -EINVAL; +} + +static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid) +{ + int cvt_idx; + + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) + if (spec->cvts[cvt_idx].cvt_nid == cvt_nid) + return cvt_idx; + + snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid); return -EINVAL; } @@ -326,28 +354,28 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); } -static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) +static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) { /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - /* Enable pin out */ + /* Disable pin out until stream is active*/ snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) { - return 1 + snd_hda_codec_read(codec, nid, 0, + return 1 + snd_hda_codec_read(codec, cvt_nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t nid, int chs) + hda_nid_t cvt_nid, int chs) { - if (chs != hdmi_get_channel_count(codec, nid)) - snd_hda_codec_write(codec, nid, 0, + if (chs != hdmi_get_channel_count(codec, cvt_nid)) + snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } @@ -384,11 +412,8 @@ static void init_channel_allocations(void) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, - int channels) +static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) { - struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld; int i; int ca = 0; int spk_mask = 0; @@ -400,19 +425,6 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, if (channels <= 2) return 0; - i = hda_node_index(spec->pin_cvt, nid); - if (i < 0) - return 0; - eld = &spec->sink_eld[i]; - - /* - * HDMI sink's ELD info cannot always be retrieved for now, e.g. - * in console or for audio devices. Assume the highest speakers - * configuration, to _not_ prohibit multi-channel audio playback. - */ - if (!eld->spk_alloc) - eld->spk_alloc = 0xffff; - /* * expand ELD's speaker allocation mask * @@ -608,67 +620,63 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return true; } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - hda_nid_t pin_nid; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + hda_nid_t pin_nid = per_pin->pin_nid; int channels = substream->runtime->channels; + struct hdmi_eld *eld; int ca; - int i; union audio_infoframe ai; - ca = hdmi_channel_allocation(codec, nid, channels); - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != nid) - continue; - if (!spec->sink_eld[i].monitor_present) - continue; + eld = &spec->pins[pin_idx].sink_eld; + if (!eld->monitor_present) + return; - pin_nid = spec->pin[i]; - - memset(&ai, 0, sizeof(ai)); - if (spec->sink_eld[i].conn_type == 0) { /* HDMI */ - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - hdmi_ai->CC02_CT47 = channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = channels - 1; - dp_ai->CA = ca; - } else { - snd_printd("HDMI: unknown connection type at pin %d\n", - pin_nid); - continue; - } + ca = hdmi_channel_allocation(eld, channels); + + memset(&ai, 0, sizeof(ai)); + if (eld->conn_type == 0) { /* HDMI */ + struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; + + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x01; + hdmi_ai->len = 0x0a; + hdmi_ai->CC02_CT47 = channels - 1; + hdmi_ai->CA = ca; + hdmi_checksum_audio_infoframe(hdmi_ai); + } else if (eld->conn_type == 1) { /* DisplayPort */ + struct dp_audio_infoframe *dp_ai = &ai.dp; + + dp_ai->type = 0x84; + dp_ai->len = 0x1b; + dp_ai->ver = 0x11 << 2; + dp_ai->CC02_CT47 = channels - 1; + dp_ai->CA = ca; + } else { + snd_printd("HDMI: unknown connection type at pin %d\n", + pin_nid); + return; + } - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - snd_printdd("hdmi_setup_audio_infoframe: " - "cvt=%d pin=%d channels=%d\n", - nid, pin_nid, - channels); - hdmi_setup_channel_mapping(codec, pin_nid, ca); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } + /* + * sizeof(ai) is used instead of sizeof(*hdmi_ai) or + * sizeof(*dp_ai) to avoid partial match/update problems when + * the user switches between HDMI/DP monitors. + */ + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, + sizeof(ai))) { + snd_printdd("hdmi_setup_audio_infoframe: " + "pin=%d channels=%d\n", + pin_nid, + channels); + hdmi_setup_channel_mapping(codec, pin_nid, ca); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, + ai.bytes, sizeof(ai)); + hdmi_start_infoframe_trans(codec, pin_nid); } } @@ -686,17 +694,27 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT; int pd = !!(res & AC_UNSOL_RES_PD); int eldv = !!(res & AC_UNSOL_RES_ELDV); - int index; + int pin_idx; + struct hdmi_eld *eld; printk(KERN_INFO - "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - pin_nid, pd, eldv); + "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, pd, eldv); - index = hda_node_index(spec->pin, pin_nid); - if (index < 0) + pin_idx = pin_nid_to_pin_index(spec, pin_nid); + if (pin_idx < 0) return; + eld = &spec->pins[pin_idx].sink_eld; - hdmi_present_sense(codec, pin_nid, &spec->sink_eld[index]); + hdmi_present_sense(codec, pin_nid, eld); + + /* + * HDMI sink's ELD info cannot always be retrieved for now, e.g. + * in console or for audio devices. Assume the highest speakers + * configuration, to _not_ prohibit multi-channel audio playback. + */ + if (!eld->spk_alloc) + eld->spk_alloc = 0xffff; } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -707,7 +725,8 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); printk(KERN_INFO - "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + "HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + codec->addr, tag, subtag, cp_state, @@ -727,7 +746,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - if (hda_node_index(spec->pin, tag) < 0) { + if (pin_nid_to_pin_index(spec, tag) < 0) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } @@ -746,21 +765,14 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) #define is_hbr_format(format) \ ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, int format) +static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) { - struct hdmi_spec *spec = codec->spec; int pinctl; int new_pinctl = 0; - int i; - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != nid) - continue; - if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR)) - continue; - - pinctl = snd_hda_codec_read(codec, spec->pin[i], 0, + if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { + pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_pinctl = pinctl & ~AC_PINCTL_EPT; @@ -771,22 +783,22 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, snd_printdd("hdmi_setup_stream: " "NID=0x%x, %spinctl=0x%x\n", - spec->pin[i], + pin_nid, pinctl == new_pinctl ? "" : "new-", new_pinctl); if (pinctl != new_pinctl) - snd_hda_codec_write(codec, spec->pin[i], 0, + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_pinctl); - } + } if (is_hbr_format(format) && !new_pinctl) { snd_printdd("hdmi_setup_stream: HBR is not supported\n"); return -EINVAL; } - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); + snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); return 0; } @@ -798,31 +810,62 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld; - struct hda_pcm_stream *codec_pars; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int idx; + int pin_idx, cvt_idx, mux_idx = 0; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int pinctl; - for (idx = 0; idx < spec->num_cvts; idx++) - if (hinfo->nid == spec->cvt[idx]) - break; - if (snd_BUG_ON(idx >= spec->num_cvts) || - snd_BUG_ON(idx >= spec->num_pins)) + /* Validate hinfo */ + pin_idx = hinfo_to_pin_index(spec, hinfo); + if (snd_BUG_ON(pin_idx < 0)) return -EINVAL; + per_pin = &spec->pins[pin_idx]; + eld = &per_pin->sink_eld; + + /* Dynamically assign converter to stream */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = &spec->cvts[cvt_idx]; - /* save the PCM info the codec provides */ - codec_pars = &spec->codec_pcm_pars[idx]; - if (!codec_pars->rates) - *codec_pars = *hinfo; + /* Must not already be assigned */ + if (per_cvt->assigned) + continue; + /* Must be in pin's mux's list of converters */ + for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) + if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) + break; + /* Not in mux list */ + if (mux_idx == per_pin->num_mux_nids) + continue; + break; + } + /* No free converters */ + if (cvt_idx == spec->num_cvts) + return -ENODEV; + + /* Claim converter */ + per_cvt->assigned = 1; + hinfo->nid = per_cvt->cvt_nid; + + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl | PIN_OUT); + snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); /* Initially set the converter's capabilities */ - hinfo->channels_min = codec_pars->channels_min; - hinfo->channels_max = codec_pars->channels_max; - hinfo->rates = codec_pars->rates; - hinfo->formats = codec_pars->formats; - hinfo->maxbps = codec_pars->maxbps; + hinfo->channels_min = per_cvt->channels_min; + hinfo->channels_max = per_cvt->channels_max; + hinfo->rates = per_cvt->rates; + hinfo->formats = per_cvt->formats; + hinfo->maxbps = per_cvt->maxbps; - eld = &spec->sink_eld[idx]; + /* Restrict capabilities by ELD if this isn't disabled */ if (!static_hdmi_pcm && eld->eld_valid) { snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || @@ -844,12 +887,11 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* * HDA/HDMI auto parsing */ -static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) { struct hdmi_spec *spec = codec->spec; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; - int conn_len, curr; - int index; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + hda_nid_t pin_nid = per_pin->pin_nid; if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { snd_printk(KERN_WARNING @@ -859,19 +901,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) return -EINVAL; } - conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, - HDA_MAX_CONNECTIONS); - if (conn_len > 1) - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - else - curr = 0; - - index = hda_node_index(spec->pin, pin_nid); - if (index < 0) - return -EINVAL; - - spec->pin_cvt[index] = conn_list[curr]; + per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid, + per_pin->mux_nids, + HDA_MAX_CONNECTIONS); return 0; } @@ -898,8 +930,8 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, eld->eld_valid = 0; printk(KERN_INFO - "HDMI status: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - pin_nid, eld->monitor_present, eld->eld_valid); + "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); if (eld->eld_valid) if (!snd_hdmi_get_eld(eld, codec, pin_nid)) @@ -911,47 +943,75 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct hdmi_spec *spec = codec->spec; + unsigned int caps, config; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; int err; - if (spec->num_pins >= MAX_HDMI_PINS) { - snd_printk(KERN_WARNING - "HDMI: no space for pin %d\n", pin_nid); + caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP); + if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) + return 0; + + config = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + if (get_defcfg_connect(config) == AC_JACK_PORT_NONE) + return 0; + + if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS)) return -E2BIG; - } + + pin_idx = spec->num_pins; + per_pin = &spec->pins[pin_idx]; + eld = &per_pin->sink_eld; + + per_pin->pin_nid = pin_nid; err = snd_hda_input_jack_add(codec, pin_nid, SND_JACK_VIDEOOUT, NULL); if (err < 0) return err; - hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); + err = hdmi_read_pin_conn(codec, pin_idx); + if (err < 0) + return err; - spec->pin[spec->num_pins] = pin_nid; spec->num_pins++; - return hdmi_read_pin_conn(codec, pin_nid); + hdmi_present_sense(codec, pin_nid, eld); + + return 0; } -static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) { - int i, found_pin = 0; struct hdmi_spec *spec = codec->spec; - - for (i = 0; i < spec->num_pins; i++) - if (nid == spec->pin_cvt[i]) { - found_pin = 1; - break; - } - - if (!found_pin) { - snd_printdd("HDMI: Skipping node %d (no connection)\n", nid); - return -EINVAL; - } + int cvt_idx; + struct hdmi_spec_per_cvt *per_cvt; + unsigned int chans; + int err; if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS)) return -E2BIG; - spec->cvt[spec->num_cvts] = nid; + chans = get_wcaps(codec, cvt_nid); + chans = get_wcaps_channels(chans); + + cvt_idx = spec->num_cvts; + per_cvt = &spec->cvts[cvt_idx]; + + per_cvt->cvt_nid = cvt_nid; + per_cvt->channels_min = 2; + if (chans <= 16) + per_cvt->channels_max = chans; + + err = snd_hda_query_supported_pcm(codec, cvt_nid, + &per_cvt->rates, + &per_cvt->formats, + &per_cvt->maxbps); + if (err < 0) + return err; + spec->num_cvts++; return 0; @@ -961,8 +1021,6 @@ static int hdmi_parse_codec(struct hda_codec *codec) { hda_nid_t nid; int i, nodes; - int num_tmp_cvts = 0; - hda_nid_t tmp_cvt[MAX_HDMI_CVTS]; nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); if (!nid || nodes < 0) { @@ -973,7 +1031,6 @@ static int hdmi_parse_codec(struct hda_codec *codec) for (i = 0; i < nodes; i++, nid++) { unsigned int caps; unsigned int type; - unsigned int config; caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); type = get_wcaps_type(caps); @@ -983,32 +1040,14 @@ static int hdmi_parse_codec(struct hda_codec *codec) switch (type) { case AC_WID_AUD_OUT: - if (num_tmp_cvts >= MAX_HDMI_CVTS) { - snd_printk(KERN_WARNING - "HDMI: no space for converter %d\n", nid); - continue; - } - tmp_cvt[num_tmp_cvts] = nid; - num_tmp_cvts++; + hdmi_add_cvt(codec, nid); break; case AC_WID_PIN: - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - continue; - - config = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); - if (get_defcfg_connect(config) == AC_JACK_PORT_NONE) - continue; - hdmi_add_pin(codec, nid); break; } } - for (i = 0; i < num_tmp_cvts; i++) - hdmi_add_cvt(codec, tmp_cvt[i]); - /* * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event * can be lost and presence sense verb will become inaccurate if the @@ -1025,7 +1064,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) /* */ -static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = { +static char *generic_hdmi_pcm_names[MAX_HDMI_PINS] = { "HDMI 0", "HDMI 1", "HDMI 2", @@ -1042,51 +1081,84 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, hinfo->nid, - substream->runtime->channels); + hda_nid_t cvt_nid = hinfo->nid; + struct hdmi_spec *spec = codec->spec; + int pin_idx = hinfo_to_pin_index(spec, hinfo); + hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid; + + hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); + hdmi_setup_audio_infoframe(codec, pin_idx, substream); - return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); + return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } -static const struct hda_pcm_stream generic_hdmi_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .ops = { - .open = hdmi_pcm_open, - .prepare = generic_hdmi_playback_pcm_prepare, - }, +static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int cvt_idx, pin_idx; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + int pinctl; + + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + + if (hinfo->nid) { + cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid); + if (snd_BUG_ON(cvt_idx < 0)) + return -EINVAL; + per_cvt = &spec->cvts[cvt_idx]; + + snd_BUG_ON(!per_cvt->assigned); + per_cvt->assigned = 0; + hinfo->nid = 0; + + pin_idx = hinfo_to_pin_index(spec, hinfo); + if (snd_BUG_ON(pin_idx < 0)) + return -EINVAL; + per_pin = &spec->pins[pin_idx]; + + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl & ~PIN_OUT); + snd_hda_spdif_ctls_unassign(codec, pin_idx); + } + + return 0; +} + +static const struct hda_pcm_ops generic_ops = { + .open = hdmi_pcm_open, + .prepare = generic_hdmi_playback_pcm_prepare, + .cleanup = generic_hdmi_playback_pcm_cleanup, }; static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - int i; + int pin_idx; - codec->num_pcms = spec->num_cvts; - codec->pcm_info = info; - - for (i = 0; i < codec->num_pcms; i++, info++) { - unsigned int chans; + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hda_pcm *info; struct hda_pcm_stream *pstr; - chans = get_wcaps(codec, spec->cvt[i]); - chans = get_wcaps_channels(chans); - - info->name = generic_hdmi_pcm_names[i]; + info = &spec->pcm_rec[pin_idx]; + info->name = generic_hdmi_pcm_names[pin_idx]; info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - if (spec->pcm_playback) - *pstr = *spec->pcm_playback; - else - *pstr = generic_hdmi_pcm_playback; - pstr->nid = spec->cvt[i]; - if (pstr->channels_max <= 2 && chans && chans <= 16) - pstr->channels_max = chans; + pstr->substreams = 1; + pstr->ops = generic_ops; + /* other pstr fields are set in open */ } + codec->num_pcms = spec->num_pins; + codec->pcm_info = spec->pcm_rec; + return 0; } @@ -1094,13 +1166,16 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int err; - int i; + int pin_idx; - for (i = 0; i < codec->num_pcms; i++) { - err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i], - spec->cvt[i]); + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + err = snd_hda_create_spdif_out_ctls(codec, + per_pin->pin_nid, + per_pin->mux_nids[0]); if (err < 0) return err; + snd_hda_spdif_ctls_unassign(codec, pin_idx); } return 0; @@ -1109,13 +1184,19 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) static int generic_hdmi_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int i; + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + hda_nid_t pin_nid = per_pin->pin_nid; + struct hdmi_eld *eld = &per_pin->sink_eld; - for (i = 0; spec->pin[i]; i++) { - hdmi_enable_output(codec, spec->pin[i]); - snd_hda_codec_write(codec, spec->pin[i], 0, + hdmi_init_pin(codec, pin_nid); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | spec->pin[i]); + AC_USRSP_EN | pin_nid); + + snd_hda_eld_proc_new(codec, eld, pin_idx); } return 0; } @@ -1123,10 +1204,14 @@ static int generic_hdmi_init(struct hda_codec *codec) static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int i; + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + struct hdmi_eld *eld = &per_pin->sink_eld; - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); + snd_hda_eld_proc_free(codec, eld); + } snd_hda_input_jack_free(codec); kfree(spec); @@ -1143,7 +1228,6 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { static int patch_generic_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; - int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -1157,9 +1241,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) } codec->patch_ops = generic_hdmi_patch_ops; - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); - init_channel_allocations(); return 0; @@ -1182,7 +1263,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec) unsigned int chans; struct hda_pcm_stream *pstr; - chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps(codec, spec->cvts[i].cvt_nid); chans = get_wcaps_channels(chans); info->name = generic_hdmi_pcm_names[i]; @@ -1190,7 +1271,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec) pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; snd_BUG_ON(!spec->pcm_playback); *pstr = *spec->pcm_playback; - pstr->nid = spec->cvt[i]; + pstr->nid = spec->cvts[i].cvt_nid; if (pstr->channels_max <= 2 && chans && chans <= 16) pstr->channels_max = chans; } @@ -1206,8 +1287,8 @@ static int simple_playback_build_controls(struct hda_codec *codec) for (i = 0; i < codec->num_pcms; i++) { err = snd_hda_create_spdif_out_ctls(codec, - spec->cvt[i], - spec->cvt[i]); + spec->cvts[i].cvt_nid, + spec->cvts[i].cvt_nid); if (err < 0) return err; } @@ -1414,7 +1495,7 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, int i; struct hdmi_spec *spec = codec->spec; struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, spec->cvt[0]); + snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); mutex_lock(&codec->spdif_mutex); @@ -1561,7 +1642,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; spec->num_cvts = 1; - spec->cvt[0] = nvhdmi_master_con_nid_7x; + spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x; spec->pcm_playback = &nvhdmi_pcm_playback_2ch; codec->patch_ops = nvhdmi_patch_ops_2ch; @@ -1612,11 +1693,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, substream); if (err < 0) return err; - snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT, - chans - 1); + snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chans - 1); /* FIXME: XXX */ for (i = 0; i < chans; i++) { - snd_hda_codec_write(codec, spec->cvt[0], 0, + snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i); } @@ -1647,8 +1728,8 @@ static int atihdmi_init(struct hda_codec *codec) snd_hda_sequence_write(codec, atihdmi_basic_init); /* SI codec requires to unmute the pin */ - if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, spec->pin[0], 0, + if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); return 0; @@ -1676,8 +1757,8 @@ static int patch_atihdmi(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.dig_out_nid = ATIHDMI_CVT_NID; spec->num_cvts = 1; - spec->cvt[0] = ATIHDMI_CVT_NID; - spec->pin[0] = ATIHDMI_PIN_NID; + spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID; + spec->pins[0].pin_nid = ATIHDMI_PIN_NID; spec->pcm_playback = &atihdmi_pcm_digital_playback; codec->patch_ops = atihdmi_patch_ops; -- cgit v1.2.3 From a810364a0424c297242c6c66071a42f7675a5568 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Jun 2011 12:23:23 +0200 Subject: ALSA: hda - Handle -1 as invalid position, too When reading from the position-buffer results in -1, handle as it's invalid and falls back to LPIB mode as well as 0. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 966f40147bc3..45cd02f1ad88 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1930,6 +1930,17 @@ static unsigned int azx_get_position(struct azx *chip, default: /* use the position buffer */ pos = le32_to_cpu(*azx_dev->posbuf); + if (chip->position_fix[stream] == POS_FIX_AUTO) { + if (!pos || pos == (u32)-1) { + printk(KERN_WARNING + "hda-intel: Invalid position buffer, " + "using LPIB read method instead.\n"); + chip->position_fix[stream] = POS_FIX_LPIB; + pos = azx_sd_readl(azx_dev, SD_LPIB); + } else + chip->position_fix[stream] = POS_FIX_POSBUF; + } + break; } if (pos >= azx_dev->bufsize) @@ -1967,16 +1978,6 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) stream = azx_dev->substream->stream; pos = azx_get_position(chip, azx_dev); - if (chip->position_fix[stream] == POS_FIX_AUTO) { - if (!pos) { - printk(KERN_WARNING - "hda-intel: Invalid position buffer, " - "using LPIB read method instead.\n"); - chip->position_fix[stream] = POS_FIX_LPIB; - pos = azx_get_position(chip, azx_dev); - } else - chip->position_fix[stream] = POS_FIX_POSBUF; - } if (WARN_ONCE(!azx_dev->period_bytes, "hda-intel: zero azx_dev->period_bytes")) -- cgit v1.2.3 From b4a655e81d4d1d12abc92d29dfb7550e66a08799 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Jun 2011 12:26:56 +0200 Subject: ALSA: hda - Judge playback stream from stream id in azx_via_get_position() Instead of checking the azx_dev index with a fixed number (4), check the stream direction of the assigned substream. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 45cd02f1ad88..5f2d05a8d0eb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1866,7 +1866,7 @@ static unsigned int azx_via_get_position(struct azx *chip, unsigned int fifo_size; link_pos = azx_sd_readl(azx_dev, SD_LPIB); - if (azx_dev->index >= 4) { + if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Playback, no problem using link position */ return link_pos; } -- cgit v1.2.3 From 695cd4a34e4e02486e35d3c8e0ee85581a619357 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 10 Jun 2011 14:37:04 +0200 Subject: ALSA: hda - Disable SPDIF only when no pin config set for HP with AD1981 Some HP laptops with AD1981 have SPDIF connections, but currently the driver disables it statically. Better to check the pin default config to judge whether to enable or disable the SPDIF. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 0f7b8951440f..1362c8ba4d1f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1922,7 +1922,8 @@ static int patch_ad1981(struct hda_codec *codec) spec->mixers[0] = ad1981_hp_mixers; spec->num_init_verbs = 2; spec->init_verbs[1] = ad1981_hp_init_verbs; - spec->multiout.dig_out_nid = 0; + if (!is_jack_available(codec, 0x0a)) + spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1981_hp_capture_source; codec->patch_ops.init = ad1981_hp_init; -- cgit v1.2.3 From 8b0bd2266f9f8f7e7f192f2a5be164d7f637ce45 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 10 Jun 2011 14:56:26 +0200 Subject: ALSA: hda - Fix SSYNC register value for non-Intel controllers SSYNC register was once defined as 0x34-37 in the old Intel datasheet, but corrected later to 0x38-3b. For fixing the register usage, a new bit-flag is introduced for indicating the old ICH SSYNC register, and ICH* PCI entries are added explicitly to enable this quirk. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 5f2d05a8d0eb..81bd3b33a15f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -177,7 +177,8 @@ MODULE_DESCRIPTION("Intel HDA driver"); #define ICH6_REG_INTCTL 0x20 #define ICH6_REG_INTSTS 0x24 #define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */ -#define ICH6_REG_SYNC 0x34 +#define ICH6_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ +#define ICH6_REG_SSYNC 0x38 #define ICH6_REG_CORBLBASE 0x40 #define ICH6_REG_CORBUBASE 0x44 #define ICH6_REG_CORBWP 0x48 @@ -479,6 +480,7 @@ enum { #define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ #define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ +#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -1795,7 +1797,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&chip->reg_lock); if (nsync > 1) { /* first, set SYNC bits of corresponding streams */ - azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits); + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + azx_writel(chip, OLD_SSYNC, + azx_readl(chip, OLD_SSYNC) | sbits); + else + azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); } snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) @@ -1851,7 +1857,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (nsync > 1) { spin_lock(&chip->reg_lock); /* reset SYNC bits */ - azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits); + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + azx_writel(chip, OLD_SSYNC, + azx_readl(chip, OLD_SSYNC) & ~sbits); + else + azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); spin_unlock(&chip->reg_lock); } return 0; @@ -2819,6 +2829,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP }, + { PCI_DEVICE(0x8086, 0x2668), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH6 */ + { PCI_DEVICE(0x8086, 0x27d8), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH7 */ + { PCI_DEVICE(0x8086, 0x269a), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ESB2 */ + { PCI_DEVICE(0x8086, 0x284b), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH8 */ + { PCI_DEVICE(0x8086, 0x293e), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH9 */ + { PCI_DEVICE(0x8086, 0x293f), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH9 */ + { PCI_DEVICE(0x8086, 0x3a3e), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH10 */ + { PCI_DEVICE(0x8086, 0x3a6e), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH10 */ /* Generic Intel */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, -- cgit v1.2.3 From 9857edfd4db0dc879f786e042f24900fd683b0ac Mon Sep 17 00:00:00 2001 From: Greg Thelen Date: Mon, 13 Jun 2011 07:45:45 -0700 Subject: ALSA: hda: check make_exec_verb() return value If given a -1 cmd parameter then make_exec_verb() returns -1 without setting the res output value. Prior to this change snd_hda_codec_read() assumed that make_exec_verb() unconditionally set res regardless of the cmd value. This change explicitly checks the make_exec_verb() return value before consuming the potentially unset res value. Signed-off-by: Greg Thelen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ce418c805a1a..a2388fc23e39 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -243,7 +243,8 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, { unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm); unsigned int res; - codec_exec_verb(codec, cmd, &res); + if (codec_exec_verb(codec, cmd, &res)) + return -1; return res; } EXPORT_SYMBOL_HDA(snd_hda_codec_read); -- cgit v1.2.3 From b13e552d374a9cbee20ba24635608289cc8a7c97 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 17 Jun 2011 16:27:01 +0200 Subject: ALSA: HDA: Remove redundant LPIB quirks for ATI chipset Now that we have changed the position_fix default for ATI and AMD to be LPIB (see commit 50e3bbf989), we can remove the quirks that were added for ATI chipsets. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 81bd3b33a15f..25619cd18831 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2361,28 +2361,20 @@ static int azx_dev_free(struct snd_device *device) * white/black-listing for position_fix */ static struct snd_pci_quirk position_fix_list[] __devinitdata = { - SND_PCI_QUIRK(0x1025, 0x009f, "Acer Aspire 5110", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1025, 0x026f, "Acer Aspire 5538", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell Inspiron 1120", POS_FIX_LPIB), SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x8410, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB), SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB), SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB), SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB), - SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), {} }; -- cgit v1.2.3 From 5f4b36d64d1f1ba1da46bee3ec4f0519dfaf68e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 14:55:02 +0200 Subject: ALSA: hda - Remove superfluous NID_MAPPING use for smart51 mixer Just a minor clean up; nid-mapping can be set directly to the smart51 mixer element. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 89a0f2a3d269..995974d0b120 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1003,19 +1003,13 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new via_smart51_mixer[2] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Smart 5.1", - .count = 1, - .info = via_smart51_info, - .get = via_smart51_get, - .put = via_smart51_put, - }, - { - .iface = NID_MAPPING, - .name = "Smart 5.1", - } +static const struct snd_kcontrol_new via_smart51_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = via_smart51_info, + .get = via_smart51_get, + .put = via_smart51_put, }; static int via_smart51_build(struct via_spec *spec) @@ -1030,17 +1024,14 @@ static int via_smart51_build(struct via_spec *spec) if (cfg->line_outs > 2) return 0; - knew = via_clone_control(spec, &via_smart51_mixer[0]); + knew = via_clone_control(spec, &via_smart51_mixer); if (knew == NULL) return -ENOMEM; for (i = 0; i < cfg->num_inputs; i++) { nid = cfg->inputs[i].pin; if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { - knew = via_clone_control(spec, &via_smart51_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = nid; + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; break; } } -- cgit v1.2.3 From 24088a58d694ca5acc31ba67f966f60385789235 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 16:59:21 +0200 Subject: ALSA: hda - Add control to suppress the dynamic pin-power for VIA Currently VIA driver controls the power-state of each pin per jack detection. But, it means that the power-state mismatch may occur when the machine doesn't give the proper jack-detection. For avoiding this problem, a new control element "Dynamic Power-Control" is provided so that user can turn on/off the pin-power control. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 995974d0b120..8a9791ab43c0 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -153,6 +153,7 @@ struct via_spec { unsigned int hp_independent_mode_index; unsigned int smart51_enabled; unsigned int dmic_enabled; + unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; /* work to check hp jack state */ @@ -605,8 +606,12 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned no_presence = (def_conf & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - unsigned present = snd_hda_jack_detect(codec, nid); struct via_spec *spec = codec->spec; + unsigned present = 0; + + no_presence |= spec->no_pin_power_ctl; + if (!no_presence) + present = snd_hda_jack_detect(codec, nid); if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { @@ -618,6 +623,55 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } +static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { + "Disabled", "Enabled" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; + return 0; +} + +static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int val = !ucontrol->value.enumerated.item[0]; + + if (val == spec->no_pin_power_ctl) + return 0; + spec->no_pin_power_ctl = val; + set_widgets_power_state(codec); + return 1; +} + +static const struct snd_kcontrol_new via_pin_power_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Dynamic Power-Control", + .info = via_pin_power_ctl_info, + .get = via_pin_power_ctl_get, + .put = via_pin_power_ctl_put, +}; + + /* * input MUX handling */ @@ -1480,6 +1534,10 @@ static int via_build_controls(struct hda_codec *codec) const struct snd_kcontrol_new *knew; int err, i; + if (spec->set_widgets_power_state) + if (!via_clone_control(spec, &via_pin_power_ctl_enum)) + return -ENOMEM; + for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) -- cgit v1.2.3 From a766d0d763bf9d64ff622db2c9c620d45a4ead96 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 09:01:29 +0200 Subject: ALSA: hda - Fill ADCs dynamically for VIA codecs Instead of giving the fixed ADC list, parse the widgets and fill in ADCs dynamically. Also, probe the stereo-mixer input more dynamically, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 244 +++++++++++++++------------------------------- 1 file changed, 80 insertions(+), 164 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 8a9791ab43c0..3a3df946661c 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -129,7 +129,7 @@ struct via_spec { /* capture */ unsigned int num_adc_nids; - const hda_nid_t *adc_nids; + hda_nid_t adc_nids[3]; hda_nid_t mux_nids[3]; hda_nid_t dig_in_nid; hda_nid_t dig_in_pin; @@ -418,51 +418,6 @@ static const struct snd_kcontrol_new via_control_templates[] = { BIND_PIN_MUTE, }; -static const hda_nid_t vt1708_adc_nids[2] = { - /* ADC1-2 */ - 0x15, 0x27 -}; - -static const hda_nid_t vt1709_adc_nids[3] = { - /* ADC1-2 */ - 0x14, 0x15, 0x16 -}; - -static const hda_nid_t vt1708B_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt1708S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt1702_adc_nids[3] = { - /* ADC1-2 */ - 0x12, 0x20, 0x1F -}; - -static const hda_nid_t vt1718S_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static const hda_nid_t vt1716S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt2002P_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static const hda_nid_t vt1812_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - /* add dynamic controls */ static int __via_add_control(struct via_spec *spec, int type, const char *name, @@ -2050,20 +2005,71 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } +static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + return -1; +} + +/* look for ADCs */ +static int via_fill_adcs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid = codec->start_nid; + int i; + + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (wcaps & AC_WCAP_DIGITAL) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) + return -ENOMEM; + spec->adc_nids[spec->num_adc_nids++] = nid; + } + return 0; +} + +static int get_mux_nids(struct hda_codec *codec); + /* create playback/capture controls for input pins */ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg, - hda_nid_t cap_nid, - const hda_nid_t pin_idxs[], - int num_idxs) + hda_nid_t mix_nid) { struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, type, type_idx = 0; + int i, err, idx, idx2, type, type_idx = 0; + hda_nid_t cap_nid; + hda_nid_t pin_idxs[8]; + int num_idxs; + + err = via_fill_adcs(codec); + if (err < 0) + return err; + err = get_mux_nids(codec); + if (err < 0) + return err; + cap_nid = spec->mux_nids[0]; + + num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, + ARRAY_SIZE(pin_idxs)); + if (num_idxs <= 0) + return 0; /* for internal loopback recording select */ for (idx = 0; idx < num_idxs; idx++) { - if (pin_idxs[idx] == 0xff) { + if (pin_idxs[idx] == mix_nid) { snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); break; } @@ -2082,14 +2088,10 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, else type_idx = 0; label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->codec_type == VT1708S || - spec->codec_type == VT1702 || - spec->codec_type == VT1716S) - err = via_new_analog_input(spec, label, type_idx, - idx+1, cap_nid); - else + idx2 = get_connection_index(codec, mix_nid, pin_idxs[idx]); + if (idx2 >= 0) err = via_new_analog_input(spec, label, type_idx, - idx, cap_nid); + idx2, mix_nid); if (err < 0) return err; snd_hda_add_imux_item(imux, label, idx, NULL); @@ -2101,9 +2103,7 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x17); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -2326,11 +2326,7 @@ static int patch_vt1708(struct hda_codec *codec) spec->stream_digital_playback = &vt1708_pcm_digital_playback; spec->stream_digital_capture = &vt1708_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1708_capture_mixer; spec->num_mixers++; } @@ -2675,9 +2671,7 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x18); } static int vt1709_parse_auto_config(struct hda_codec *codec) @@ -2764,11 +2758,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1709_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1709_capture_mixer; spec->num_mixers++; } @@ -2856,11 +2846,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1709_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1709_capture_mixer; spec->num_mixers++; } @@ -3207,9 +3193,7 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); } static int vt1708B_parse_auto_config(struct hda_codec *codec) @@ -3380,10 +3364,7 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1708B_pcm_digital_playback; spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708B_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; spec->num_mixers++; } @@ -3432,10 +3413,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1708B_pcm_digital_playback; spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708B_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; spec->num_mixers++; } @@ -3771,9 +3749,7 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); } /* fill out digital output widgets; one for master and one for slave outputs */ @@ -3909,10 +3885,7 @@ static int patch_vt1708S(struct hda_codec *codec) spec->stream_name_digital = "VT1708S Digital"; spec->stream_digital_playback = &vt1708S_pcm_digital_playback; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; @@ -4148,9 +4121,7 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a); } static int vt1702_parse_auto_config(struct hda_codec *codec) @@ -4269,10 +4240,7 @@ static int patch_vt1702(struct hda_codec *codec) spec->stream_name_digital = "VT1702 Digital"; spec->stream_digital_playback = &vt1702_pcm_digital_playback; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1702_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1702_capture_mixer; spec->num_mixers++; } @@ -4568,9 +4536,7 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); } static int vt1718S_parse_auto_config(struct hda_codec *codec) @@ -4736,10 +4702,7 @@ static int patch_vt1718S(struct hda_codec *codec) if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) spec->stream_digital_capture = &vt1718S_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1718S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; @@ -5099,9 +5062,7 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); + return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); } static int vt1716S_parse_auto_config(struct hda_codec *codec) @@ -5278,10 +5239,7 @@ static int patch_vt1716S(struct hda_codec *codec) spec->stream_name_digital = "VT1716S Digital"; spec->stream_digital_playback = &vt1716S_pcm_digital_playback; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1716S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; @@ -5572,24 +5530,7 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; - int err; - - err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (err < 0) - return err; - /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); - if (err < 0) - return err; - - /* for digital mic select */ - snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); - - return 0; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); } static int vt2002P_parse_auto_config(struct hda_codec *codec) @@ -5802,10 +5743,7 @@ static int patch_vt2002P(struct hda_codec *codec) spec->stream_name_digital = "VT2002P Digital"; spec->stream_digital_playback = &vt2002P_pcm_digital_playback; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt2002P_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; @@ -6021,25 +5959,7 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; - int err; - - err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (err < 0) - return err; - - /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); - if (err < 0) - return err; - - /* for digital mic select */ - snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); - - return 0; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); } static int vt1812_parse_auto_config(struct hda_codec *codec) @@ -6217,11 +6137,7 @@ static int patch_vt1812(struct hda_codec *codec) spec->stream_name_digital = "VT1812 Digital"; spec->stream_digital_playback = &vt1812_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1812_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); - get_mux_nids(codec); + if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1812_capture_mixer; -- cgit v1.2.3 From e06e5a297474c8027beffe10541981845ca0c98b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 15:46:13 +0200 Subject: ALSA: hda - Defer mixer element creation to the right time in patch_via.c The jack-detect control should be created at the time of build_controls callback instead of calling snd_hda_add_ctls() at the tree-parsing time. For that, copy the control to the temporary array like other cases. Also, fixed typos of vt1708_jack_detect in all places. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 3a3df946661c..30d1273f3c3a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -159,7 +159,7 @@ struct via_spec { /* work to check hp jack state */ struct hda_codec *codec; struct delayed_work vt1708_hp_work; - int vt1708_jack_detectect; + int vt1708_jack_detect; int vt1708_hp_present; void (*set_widgets_power_state)(struct hda_codec *codec); @@ -264,7 +264,7 @@ static void vt1708_start_hp_work(struct via_spec *spec) if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) return; snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); + !spec->vt1708_jack_detect); if (!delayed_work_pending(&spec->vt1708_hp_work)) schedule_delayed_work(&spec->vt1708_hp_work, msecs_to_jiffies(100)); @@ -278,7 +278,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec) && !is_aa_path_mute(spec->codec)) return; snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); + !spec->vt1708_jack_detect); cancel_delayed_work_sync(&spec->vt1708_hp_work); } @@ -2133,7 +2133,7 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) return; } -static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, +static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -2141,13 +2141,13 @@ static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, if (spec->codec_type != VT1708) return 0; - spec->vt1708_jack_detectect = + spec->vt1708_jack_detect = !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); - ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; + ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; return 0; } -static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, +static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -2156,26 +2156,23 @@ static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, if (spec->codec_type != VT1708) return 0; - spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; + spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) - == !spec->vt1708_jack_detectect; - if (spec->vt1708_jack_detectect) { + == !spec->vt1708_jack_detect; + if (spec->vt1708_jack_detect) { mute_aa_path(codec, 1); notify_aa_path_ctls(codec); } return change; } -static const struct snd_kcontrol_new vt1708_jack_detectect[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Jack Detect", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = vt1708_jack_detectect_get, - .put = vt1708_jack_detectect_put, - }, - {} /* end */ +static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Jack Detect", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = vt1708_jack_detect_get, + .put = vt1708_jack_detect_put, }; static int vt1708_parse_auto_config(struct hda_codec *codec) @@ -2206,9 +2203,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; /* add jack detect on/off control */ - err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); - if (err < 0) - return err; + if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) + return -ENOMEM; spec->multiout.max_channels = spec->multiout.num_dacs * 2; -- cgit v1.2.3 From 291c9e33bf3f8ac201b24b8f9e481756d43d7df7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 16:15:26 +0200 Subject: ALSA: hda - Refactor ctl array handling in patch_via.c No functional change. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 30d1273f3c3a..41398b07ba8b 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -420,18 +420,34 @@ static const struct snd_kcontrol_new via_control_templates[] = { /* add dynamic controls */ -static int __via_add_control(struct via_spec *spec, int type, const char *name, - int idx, unsigned long val) +static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, + const struct snd_kcontrol_new *tmpl, + const char *name) { struct snd_kcontrol_new *knew; snd_array_init(&spec->kctls, sizeof(*knew), 32); knew = snd_array_new(&spec->kctls); if (!knew) - return -ENOMEM; - *knew = via_control_templates[type]; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) + return NULL; + *knew = *tmpl; + if (!name) + name = tmpl->name; + if (name) { + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return NULL; + } + return knew; +} + +static int __via_add_control(struct via_spec *spec, int type, const char *name, + int idx, unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = __via_clone_ctl(spec, &via_control_templates[type], name); + if (!knew) return -ENOMEM; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; @@ -442,21 +458,7 @@ static int __via_add_control(struct via_spec *spec, int type, const char *name, #define via_add_control(spec, type, name, val) \ __via_add_control(spec, type, name, 0, val) -static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, - const struct snd_kcontrol_new *tmpl) -{ - struct snd_kcontrol_new *knew; - - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *tmpl; - knew->name = kstrdup(tmpl->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; -} +#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) static void via_free_kctls(struct hda_codec *codec) { -- cgit v1.2.3 From 82673bc8950b869f01f9fd517f1c2286e0e49f44 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 16:24:21 +0200 Subject: ALSA: hda - Generate PCM names dynamically in patch_via.c This reduces lots of static strings. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 64 ++++++----------------------------------------- 1 file changed, 7 insertions(+), 57 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 41398b07ba8b..c66ff69eccf2 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -115,11 +115,11 @@ struct via_spec { const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; - char *stream_name_analog; + char stream_name_analog[32]; const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; - char *stream_name_digital; + char stream_name_digital[32]; const struct hda_pcm_stream *stream_digital_playback; const struct hda_pcm_stream *stream_digital_capture; @@ -1556,6 +1556,8 @@ static int via_build_pcms(struct hda_codec *codec) codec->num_pcms = 1; codec->pcm_info = info; + snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); @@ -1570,6 +1572,9 @@ static int via_build_pcms(struct hda_codec *codec) if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; info++; + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); info->name = spec->stream_name_digital; info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { @@ -2313,14 +2318,12 @@ static int patch_vt1708(struct hda_codec *codec) } - spec->stream_name_analog = "VT1708 Analog"; spec->stream_analog_playback = &vt1708_pcm_analog_playback; /* disable 32bit format on VT1708 */ if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; spec->stream_analog_capture = &vt1708_pcm_analog_capture; - spec->stream_name_digital = "VT1708 Digital"; spec->stream_digital_playback = &vt1708_pcm_digital_playback; spec->stream_digital_capture = &vt1708_pcm_digital_capture; @@ -2748,11 +2751,9 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - spec->stream_name_analog = "VT1709 Analog"; spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; spec->stream_analog_capture = &vt1709_pcm_analog_capture; - spec->stream_name_digital = "VT1709 Digital"; spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; @@ -2836,11 +2837,9 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - spec->stream_name_analog = "VT1709 Analog"; spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; spec->stream_analog_capture = &vt1709_pcm_analog_capture; - spec->stream_name_digital = "VT1709 Digital"; spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; @@ -3354,11 +3353,9 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - spec->stream_name_analog = "VT1708B Analog"; spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - spec->stream_name_digital = "VT1708B Digital"; spec->stream_digital_playback = &vt1708B_pcm_digital_playback; spec->stream_digital_capture = &vt1708B_pcm_digital_capture; @@ -3403,11 +3400,9 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - spec->stream_name_analog = "VT1708B Analog"; spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - spec->stream_name_digital = "VT1708B Digital"; spec->stream_digital_playback = &vt1708B_pcm_digital_playback; spec->stream_digital_capture = &vt1708B_pcm_digital_capture; @@ -3863,24 +3858,12 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - if (codec->vendor_id == 0x11060440) - spec->stream_name_analog = "VT1818S Analog"; - else if (codec->vendor_id == 0x11064397) - spec->stream_name_analog = "VT1705 Analog"; - else - spec->stream_name_analog = "VT1708S Analog"; if (codec->vendor_id == 0x11064397) spec->stream_analog_playback = &vt1705_pcm_analog_playback; else spec->stream_analog_playback = &vt1708S_pcm_analog_playback; spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - if (codec->vendor_id == 0x11060440) - spec->stream_name_digital = "VT1818S Digital"; - else if (codec->vendor_id == 0x11064397) - spec->stream_name_digital = "VT1705 Digital"; - else - spec->stream_name_digital = "VT1708S Digital"; spec->stream_digital_playback = &vt1708S_pcm_digital_playback; if (spec->adc_nids && spec->input_mux) { @@ -3905,13 +3888,6 @@ static int patch_vt1708S(struct hda_codec *codec) snprintf(codec->bus->card->mixername, sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); - spec->stream_name_analog = "VT1708BCE Analog"; - spec->stream_name_digital = "VT1708BCE Digital"; - } - /* correct names for VT1818S */ - if (codec->vendor_id == 0x11060440) { - spec->stream_name_analog = "VT1818S Analog"; - spec->stream_name_digital = "VT1818S Digital"; } /* correct names for VT1705 */ if (codec->vendor_id == 0x11064397) { @@ -4231,11 +4207,9 @@ static int patch_vt1702(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; - spec->stream_name_analog = "VT1702 Analog"; spec->stream_analog_playback = &vt1702_pcm_analog_playback; spec->stream_analog_capture = &vt1702_pcm_analog_capture; - spec->stream_name_digital = "VT1702 Digital"; spec->stream_digital_playback = &vt1702_pcm_digital_playback; if (spec->adc_nids && spec->input_mux) { @@ -4681,21 +4655,9 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - if (codec->vendor_id == 0x11060441) - spec->stream_name_analog = "VT2020 Analog"; - else if (codec->vendor_id == 0x11064441) - spec->stream_name_analog = "VT1828S Analog"; - else - spec->stream_name_analog = "VT1718S Analog"; spec->stream_analog_playback = &vt1718S_pcm_analog_playback; spec->stream_analog_capture = &vt1718S_pcm_analog_capture; - if (codec->vendor_id == 0x11060441) - spec->stream_name_digital = "VT2020 Digital"; - else if (codec->vendor_id == 0x11064441) - spec->stream_name_digital = "VT1828S Digital"; - else - spec->stream_name_digital = "VT1718S Digital"; spec->stream_digital_playback = &vt1718S_pcm_digital_playback; if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) spec->stream_digital_capture = &vt1718S_pcm_digital_capture; @@ -5230,11 +5192,9 @@ static int patch_vt1716S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; - spec->stream_name_analog = "VT1716S Analog"; spec->stream_analog_playback = &vt1716S_pcm_analog_playback; spec->stream_analog_capture = &vt1716S_pcm_analog_capture; - spec->stream_name_digital = "VT1716S Digital"; spec->stream_digital_playback = &vt1716S_pcm_digital_playback; if (spec->adc_nids && spec->input_mux) { @@ -5728,17 +5688,9 @@ static int patch_vt2002P(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; - if (spec->codec_type == VT1802) - spec->stream_name_analog = "VT1802 Analog"; - else - spec->stream_name_analog = "VT2002P Analog"; spec->stream_analog_playback = &vt2002P_pcm_analog_playback; spec->stream_analog_capture = &vt2002P_pcm_analog_capture; - if (spec->codec_type == VT1802) - spec->stream_name_digital = "VT1802 Digital"; - else - spec->stream_name_digital = "VT2002P Digital"; spec->stream_digital_playback = &vt2002P_pcm_digital_playback; if (spec->adc_nids && spec->input_mux) { @@ -6128,11 +6080,9 @@ static int patch_vt1812(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; - spec->stream_name_analog = "VT1812 Analog"; spec->stream_analog_playback = &vt1812_pcm_analog_playback; spec->stream_analog_capture = &vt1812_pcm_analog_capture; - spec->stream_name_digital = "VT1812 Digital"; spec->stream_digital_playback = &vt1812_pcm_digital_playback; if (spec->adc_nids && spec->input_mux) { -- cgit v1.2.3 From 3e0693e278ae2000cff0c9250074591696caedbf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 16:37:45 +0200 Subject: ALSA: hda - Change pin-ctl for auto-muting in patch_via.c Mute the outputs via pin-controls instead of amps for the auto-mute handling. This makes our life easier as it avoids conflict of the states between the mixer elements and the auto-mute toggles. With this change, we can use vmaster for the master control easily now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 64 ++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c66ff69eccf2..d374e8cfdcc8 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1615,17 +1615,10 @@ static void via_hp_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) { - struct snd_ctl_elem_id id; /* auto mute */ - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - /* notify change */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Front Playback Switch"); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); + snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); } } @@ -1643,17 +1636,18 @@ static void via_mono_automute(struct hda_codec *codec) /* Mute Mono Out if Line Out is plugged */ if (lineout_present) { - snd_hda_codec_amp_stereo( - codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_write(codec, 0x2A, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + lineout_present ? 0 : PIN_OUT); return; } hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) - snd_hda_codec_amp_stereo( - codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, - hp_present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, 0x2A, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + hp_present ? 0 : PIN_OUT); } static void via_gpio_control(struct hda_codec *codec) @@ -1678,9 +1672,9 @@ static void via_gpio_control(struct hda_codec *codec) if (gpio_data == 0x02) { /* unmute line out */ - snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - + snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); if (vol_counter & 0x20) { /* decrease volume */ if (vol > master_vol) @@ -1697,10 +1691,9 @@ static void via_gpio_control(struct hda_codec *codec) } } else if (!(gpio_data & 0x02)) { /* mute line out */ - snd_hda_codec_amp_stereo(codec, - spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, - HDA_AMP_MUTE); + snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + 0); } } @@ -1716,16 +1709,9 @@ static void via_speaker_automute(struct hda_codec *codec) hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) { - struct snd_ctl_elem_id id; - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, - HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); - /* notify change */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Speaker Playback Switch"); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); + snd_hda_codec_write(codec, spec->autocfg.speaker_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + hp_present ? 0 : PIN_OUT); } } @@ -1749,18 +1735,18 @@ static void via_hp_bind_automute(struct hda_codec *codec) if (!spec->hp_independent_mode) { /* Mute Line-Outs */ for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[i], - HDA_OUTPUT, 0, - HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, + spec->autocfg.line_out_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + hp_present ? 0 : PIN_OUT); if (hp_present) present = hp_present; } /* Speakers */ for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, spec->autocfg.speaker_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); } -- cgit v1.2.3 From 64be285b669e5eed65fb3630f1b2b549447b9f1e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 16:51:39 +0200 Subject: ALSA: hda - Auto-mute all LO and speakers in patch_via.c Muting all line-outs and/or speakers is more common in other drivers, so we should follow it, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 63 +++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d374e8cfdcc8..b9bd4d1cc860 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1606,6 +1606,17 @@ static void via_free(struct hda_codec *codec) kfree(codec->spec); } +/* mute/unmute outputs */ +static void toggle_output_mutes(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool mute) +{ + int i; + for (i = 0; i < num_pins; i++) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + mute ? 0 : PIN_OUT); +} + /* mute internal speaker if HP is plugged */ static void via_hp_automute(struct hda_codec *codec) { @@ -1614,12 +1625,10 @@ static void via_hp_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - if (!spec->hp_independent_mode) { - /* auto mute */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - present ? 0 : PIN_OUT); - } + if (!spec->hp_independent_mode) + toggle_output_mutes(codec, spec->autocfg.line_outs, + spec->autocfg.line_out_pins, + present); } /* mute mono out if HP or Line out is plugged */ @@ -1708,45 +1717,35 @@ static void via_speaker_automute(struct hda_codec *codec) hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - if (!spec->hp_independent_mode) { - snd_hda_codec_write(codec, spec->autocfg.speaker_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - hp_present ? 0 : PIN_OUT); - } + if (!spec->hp_independent_mode) + toggle_output_mutes(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + hp_present); } /* mute line-out and internal speaker if HP is plugged */ static void via_hp_bind_automute(struct hda_codec *codec) { - /* use long instead of int below just to avoid an internal compiler - * error with gcc 4.0.x - */ - unsigned long hp_present, present = 0; + int present; struct via_spec *spec = codec->spec; - int i; if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) return; - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); + if (!spec->hp_independent_mode) + toggle_output_mutes(codec, spec->autocfg.line_outs, + spec->autocfg.line_out_pins, + present); - present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); + if (!present) + present = snd_hda_jack_detect(codec, + spec->autocfg.line_out_pins[0]); - if (!spec->hp_independent_mode) { - /* Mute Line-Outs */ - for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_write(codec, - spec->autocfg.line_out_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - hp_present ? 0 : PIN_OUT); - if (hp_present) - present = hp_present; - } /* Speakers */ - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_write(codec, spec->autocfg.speaker_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - present ? 0 : PIN_OUT); + toggle_output_mutes(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + present); } -- cgit v1.2.3 From 620e2b28b7840f54da243ab3c771bcce5324bd80 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 17:19:19 +0200 Subject: ALSA: hda - Unify input-volume creations in patch_via.c Now storing the analog-mixer widget in spec, we can simplify the rest parts. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 132 ++++++++++++++-------------------------------- 1 file changed, 40 insertions(+), 92 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b9bd4d1cc860..3704f2b024ec 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -131,6 +131,7 @@ struct via_spec { unsigned int num_adc_nids; hda_nid_t adc_nids[3]; hda_nid_t mux_nids[3]; + hda_nid_t aa_mix_nid; hda_nid_t dig_in_nid; hda_nid_t dig_in_pin; @@ -873,20 +874,17 @@ static void notify_aa_path_ctls(struct hda_codec *codec) static void mute_aa_path(struct hda_codec *codec, int mute) { struct via_spec *spec = codec->spec; - hda_nid_t nid_mixer; int start_idx; int end_idx; int i; /* get nid of MW0 and start & end index */ switch (spec->codec_type) { case VT1708: - nid_mixer = 0x17; start_idx = 2; end_idx = 4; break; case VT1709_10CH: case VT1709_6CH: - nid_mixer = 0x18; start_idx = 2; end_idx = 4; break; @@ -894,12 +892,10 @@ static void mute_aa_path(struct hda_codec *codec, int mute) case VT1708B_4CH: case VT1708S: case VT1716S: - nid_mixer = 0x16; start_idx = 2; end_idx = 4; break; case VT1718S: - nid_mixer = 0x21; start_idx = 1; end_idx = 3; break; @@ -909,7 +905,7 @@ static void mute_aa_path(struct hda_codec *codec, int mute) /* check AA path's mute status */ for (i = start_idx; i <= end_idx; i++) { int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; - snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, + snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, HDA_AMP_MUTE, val); } } @@ -1075,7 +1071,6 @@ static const struct snd_kcontrol_new vt1708_capture_mixer[] = { static int is_aa_path_mute(struct hda_codec *codec) { int mute = 1; - hda_nid_t nid_mixer; int start_idx; int end_idx; int i; @@ -1086,24 +1081,20 @@ static int is_aa_path_mute(struct hda_codec *codec) case VT1708B_4CH: case VT1708S: case VT1716S: - nid_mixer = 0x16; start_idx = 2; end_idx = 4; break; case VT1702: - nid_mixer = 0x1a; start_idx = 1; end_idx = 3; break; case VT1718S: - nid_mixer = 0x21; start_idx = 1; end_idx = 3; break; case VT2002P: case VT1812: case VT1802: - nid_mixer = 0x21; start_idx = 0; end_idx = 2; break; @@ -1113,15 +1104,15 @@ static int is_aa_path_mute(struct hda_codec *codec) /* check AA path's mute status */ for (i = start_idx; i <= end_idx; i++) { unsigned int con_list = snd_hda_codec_read( - codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); + codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); int shift = 8 * (i % 4); hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { /* check mute status while the pin is connected */ - int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, + int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, HDA_INPUT, i) >> 7; - int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, + int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, HDA_INPUT, i) >> 7; if (!mute_l || !mute_r) { mute = 0; @@ -2035,9 +2026,8 @@ static int via_fill_adcs(struct hda_codec *codec) static int get_mux_nids(struct hda_codec *codec); /* create playback/capture controls for input pins */ -static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - hda_nid_t mix_nid) +static int via_auto_create_analog_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; @@ -2061,7 +2051,7 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, /* for internal loopback recording select */ for (idx = 0; idx < num_idxs; idx++) { - if (pin_idxs[idx] == mix_nid) { + if (pin_idxs[idx] == spec->aa_mix_nid) { snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); break; } @@ -2080,10 +2070,11 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, else type_idx = 0; label = hda_get_autocfg_input_label(codec, cfg, i); - idx2 = get_connection_index(codec, mix_nid, pin_idxs[idx]); + idx2 = get_connection_index(codec, spec->aa_mix_nid, + pin_idxs[idx]); if (idx2 >= 0) err = via_new_analog_input(spec, label, type_idx, - idx2, mix_nid); + idx2, spec->aa_mix_nid); if (err < 0) return err; snd_hda_add_imux_item(imux, label, idx, NULL); @@ -2091,13 +2082,6 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, return 0; } -/* create playback/capture controls for input pins */ -static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x17); -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1708_loopbacks[] = { { 0x17, HDA_INPUT, 1 }, @@ -2191,7 +2175,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; /* add jack detect on/off control */ @@ -2292,6 +2276,8 @@ static int patch_vt1708(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x17; + /* automatic parse from the BIOS config */ err = vt1708_parse_auto_config(codec); if (err < 0) { @@ -2653,13 +2639,6 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x18); -} - static int vt1709_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2680,7 +2659,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -2724,6 +2703,8 @@ static int patch_vt1709_10ch(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x18; + err = vt1709_parse_auto_config(codec); if (err < 0) { via_free(codec); @@ -2810,6 +2791,8 @@ static int patch_vt1709_6ch(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x18; + err = vt1709_parse_auto_config(codec); if (err < 0) { via_free(codec); @@ -3171,13 +3154,6 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); -} - static int vt1708B_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3198,7 +3174,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -3325,6 +3301,8 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x16; + /* automatic parse from the BIOS config */ err = vt1708B_parse_auto_config(codec); if (err < 0) { @@ -3723,13 +3701,6 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); -} - /* fill out digital output widgets; one for master and one for slave outputs */ static void fill_dig_outs(struct hda_codec *codec) { @@ -3775,7 +3746,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -3825,6 +3796,8 @@ static int patch_vt1708S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x16; + /* automatic parse from the BIOS config */ err = vt1708S_parse_auto_config(codec); if (err < 0) { @@ -4076,13 +4049,6 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a); -} - static int vt1702_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4109,7 +4075,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | (1 << AC_AMPCAP_MUTE_SHIFT)); - err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4179,6 +4145,8 @@ static int patch_vt1702(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x1a; + /* automatic parse from the BIOS config */ err = vt1702_parse_auto_config(codec); if (err < 0) { @@ -4489,13 +4457,6 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); -} - static int vt1718S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4517,7 +4478,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4627,6 +4588,8 @@ static int patch_vt1718S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x21; + /* automatic parse from the BIOS config */ err = vt1718S_parse_auto_config(codec); if (err < 0) { @@ -5003,13 +4966,6 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); -} - static int vt1716S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5030,7 +4986,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -5164,6 +5120,8 @@ static int patch_vt1716S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x16; + /* automatic parse from the BIOS config */ err = vt1716S_parse_auto_config(codec); if (err < 0) { @@ -5469,13 +5427,6 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); -} - static int vt2002P_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5499,7 +5450,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -5649,6 +5600,8 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x21; + /* automatic parse from the BIOS config */ err = vt2002P_parse_auto_config(codec); if (err < 0) { @@ -5890,13 +5843,6 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); -} - static int vt1812_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5920,7 +5866,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -6051,6 +5997,8 @@ static int patch_vt1812(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x21; + /* automatic parse from the BIOS config */ err = vt1812_parse_auto_config(codec); if (err < 0) { -- cgit v1.2.3 From 4a79616d079f833714c9ef63a9b825caacafe675 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 17:53:38 +0200 Subject: ALSA: hda - Unify output-control parsing in patch_via.c Parse the output-paths more dynamically, i.e. traverse the paths from each output pin instead of fixed assignment for each codec. Now all codecs are using the same output parser code. The smart51 setup doesn't work with this change, and will be fixed in the next commits. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 1378 ++++++--------------------------------------- 1 file changed, 186 insertions(+), 1192 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 3704f2b024ec..1a3618599dec 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -107,6 +107,12 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) +struct nid_path { + int depth; + hda_nid_t path[5]; + short idx[5]; +}; + struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; @@ -127,6 +133,10 @@ struct via_spec { struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; + struct nid_path out_path[4]; + struct nid_path hp_path; + struct nid_path hp_dep_path; + /* capture */ unsigned int num_adc_nids; hda_nid_t adc_nids[3]; @@ -1822,130 +1832,165 @@ static const struct hda_codec_ops via_patch_ops = { #endif }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (spec->multiout.dac_nids[i] == dac) + return false; + } + if (spec->multiout.hp_nid == dac) + return false; + return true; +} + +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, struct nid_path *path, + int depth, int wid_type) +{ + hda_nid_t conn[8]; + int i, nums; + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) + continue; + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { + path->path[depth] = conn[i]; + path->idx[depth] = i; + path->depth = ++depth; + return true; + } + } + if (depth > 4) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT || + (wid_type != -1 && type != wid_type)) + continue; + if (parse_output_path(codec, conn[i], target_dac, + path, depth + 1, AC_WID_AUD_SEL)) { + path->path[depth] = conn[i]; + path->idx[depth] = i; + return true; + } + } + return false; +} + +static int via_auto_fill_dac_nids(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; int i; hda_nid_t nid; + spec->multiout.dac_nids = spec->private_dac_nids; spec->multiout.num_dacs = cfg->line_outs; + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + if (!nid) + continue; + if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) + spec->private_dac_nids[i] = + spec->out_path[i].path[spec->out_path[i].depth - 1]; + } + return 0; +} - spec->multiout.dac_nids = spec->private_dac_nids; +static int create_ch_ctls(struct hda_codec *codec, const char *pfx, + hda_nid_t pin, hda_nid_t dac, int chs) +{ + struct via_spec *spec = codec->spec; + char name[32]; + hda_nid_t nid; + int err; - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0x13; - break; - } - } + if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = dac; + else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = pin; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Volume", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; } + if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) + nid = dac; + else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) + nid = pin; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Switch", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } return 0; } +static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid); + /* add playback controls from the parsed DAC table */ -static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static int via_auto_create_multi_out_ctls(struct hda_codec *codec) { - char name[32]; + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; static const char * const chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; - int i, err; + int i, idx, err; - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t pin, dac; + pin = cfg->line_out_pins[i]; + dac = spec->multiout.dac_nids[i]; + if (!pin || !dac) continue; - - nid_vol = nid_vols[i]; - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); + err = create_ch_ctls(codec, "Center", pin, dac, 1); if (err < 0) return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); + err = create_ch_ctls(codec, "LFE", pin, dac, 2); if (err < 0) return err; } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); + err = create_ch_ctls(codec, chname[i], pin, dac, 3); if (err < 0) return err; } } + idx = get_connection_index(codec, spec->aa_mix_nid, + spec->multiout.dac_nids[0]); + if (idx >= 0) { + /* add control to mixer */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "PCM Playback Volume", + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "PCM Playback Switch", + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; } @@ -1962,29 +2007,30 @@ static void create_hp_imux(struct via_spec *spec) spec->hp_mux = &spec->private_imux[1]; } -static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { + struct via_spec *spec = codec->spec; + hda_nid_t dac = 0; int err; if (!pin) return 0; - spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + &spec->hp_dep_path, 0, -1)) + return 0; + if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { + dac = spec->hp_path.path[spec->hp_path.depth - 1]; + spec->multiout.hp_nid = dac; + spec->hp_independent_mode_index = + spec->hp_path.idx[spec->hp_path.depth - 1]; + create_hp_imux(spec); + } - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + err = create_ch_ctls(codec, "Headphone", pin, dac, 3); if (err < 0) return err; - create_hp_imux(spec); - return 0; } @@ -2163,16 +2209,16 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -2437,208 +2483,6 @@ static const struct hda_pcm_stream vt1709_pcm_digital_capture = { .channels_max = 2, }; -static int vt1709_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - if (cfg->line_outs == 4) /* 10 channels */ - spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ - else if (cfg->line_outs == 3) /* 6 channels */ - spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ - - spec->multiout.dac_nids = spec->private_dac_nids; - - if (cfg->line_outs == 4) { /* 10 channels */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - /* AOW0 */ - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - /* AOW2 */ - spec->private_dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - /* AOW3 */ - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - /* AOW1 */ - spec->private_dac_nids[i] = 0x27; - break; - default: - break; - } - } - } - spec->private_dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ - - } else if (cfg->line_outs == 3) { /* 6 channels */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - /* AOW0 */ - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - /* AOW2 */ - spec->private_dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - /* AOW1 */ - spec->private_dac_nids[i] = 0x11; - break; - default: - break; - } - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* ADD control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_SURROUND) { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_SIDE) { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - if (spec->multiout.num_dacs == 5) /* 10 channels */ - spec->multiout.hp_nid = VT1709_HP_DAC_NID; - else if (spec->multiout.num_dacs == 3) /* 6 channels */ - spec->multiout.hp_nid = 0; - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - static int vt1709_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2647,16 +2491,16 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -3000,160 +2844,6 @@ static const struct hda_pcm_stream vt1708B_pcm_digital_capture = { .channels_max = 2, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0x24; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0x25; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; - hda_nid_t nid, nid_vol = 0; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - - return 0; -} - static int vt1708B_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3162,16 +2852,16 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -3516,200 +3206,15 @@ static const struct hda_pcm_stream vt1708S_pcm_digital_playback = { }, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +/* fill out digital output widgets; one for master and one for slave outputs */ +static void fill_dig_outs(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; int i; - hda_nid_t nid; - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - if (spec->codec->vendor_id == 0x11064397) - spec->private_dac_nids[i] = 0x25; - else - spec->private_dac_nids[i] = 0x24; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0x25; - break; - } - } - } - - /* for Smart 5.1, line/mic inputs double as output pins */ - if (cfg->line_outs == 1) { - spec->multiout.num_dacs = 3; - spec->private_dac_nids[AUTO_SEQ_SURROUND] = 0x11; - if (spec->codec->vendor_id == 0x11064397) - spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x25; - else - spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x24; - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct via_spec *spec = codec->spec; - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25}, - {0x10, 0x11, 0x25, 0} }; - hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27}, - {0x1C, 0x18, 0x27, 0} }; - hda_nid_t nid, nid_vol, nid_mute; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - /* for Smart 5.1, there are always at least six channels */ - if (!nid && i > AUTO_SEQ_CENLFE) - continue; - - if (codec->vendor_id == 0x11064397) { - nid_vol = nid_vols[1][i]; - nid_mute = nid_mutes[1][i]; - } else { - nid_vol = nid_vols[0][i]; - nid_mute = nid_mutes[0][i]; - } - if (!nid_vol && !nid_mute) - continue; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, - 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, - 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* Front */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, - 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, - 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - - return 0; -} - -/* fill out digital output widgets; one for master and one for slave outputs */ -static void fill_dig_outs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t nid; - int conn; + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t nid; + int conn; nid = spec->autocfg.dig_out_pins[i]; if (!nid) @@ -3734,16 +3239,16 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -3966,89 +3471,6 @@ static const struct hda_pcm_stream vt1702_pcm_digital_playback = { }, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1702_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - - if (cfg->line_out_pins[0]) { - /* config dac list */ - spec->private_dac_nids[0] = 0x10; - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; - - if (!cfg->line_out_pins[0]) - return -1; - - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - - /* Front */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err, i; - struct hda_input_mux *imux; - static const char * const texts[] = { "ON", "OFF", NULL}; - if (!pin) - return 0; - spec->multiout.hp_nid = 0x1D; - spec->hp_independent_mode_index = 0; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - imux = &spec->private_imux[1]; - - /* for hp mode select */ - for (i = 0; texts[i]; i++) - snd_hda_add_imux_item(imux, texts[i], i, NULL); - - spec->hp_mux = &spec->private_imux[1]; - return 0; -} - static int vt1702_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4057,16 +3479,16 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; /* limit AA path volume to 0 dB */ @@ -4313,150 +3735,6 @@ static const struct hda_pcm_stream vt1718S_pcm_digital_capture = { .channels_max = 2, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x8; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0xa; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x9; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0xb; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; - hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; - hda_nid_t nid, nid_vol, nid_mute = 0; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* Front */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0xc; /* AOW4 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - static int vt1718S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4466,16 +3744,16 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -4813,159 +4091,6 @@ static const struct hda_pcm_stream vt1716S_pcm_digital_playback = { }, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 3; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0x25; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[3] = { - "Front", "Surround", "C/LFE" - }; - hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; - hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; - hda_nid_t nid, nid_vol, nid_mute; - int i, err; - - for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x25; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - static int vt1716S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4974,16 +4099,16 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -5359,74 +4484,6 @@ static const struct hda_pcm_stream vt2002P_pcm_digital_playback = { }, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - if (cfg->line_out_pins[0]) - spec->private_dac_nids[0] = 0x8; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; - hda_nid_t sw_nid; - - if (!cfg->line_out_pins[0]) - return -1; - - if (spec->codec_type == VT1802) - sw_nid = 0x28; - else - sw_nid = 0x26; - - /* Line-Out: PortE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x9; - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL( - spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - static int vt2002P_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5437,17 +4494,17 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) return 0; /* can't find valid BIOS pin config */ - err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -5779,69 +4836,6 @@ static const struct hda_pcm_stream vt1812_pcm_digital_playback = { .cleanup = via_dig_playback_pcm_cleanup }, }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1812_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - if (cfg->line_out_pins[0]) - spec->private_dac_nids[0] = 0x8; - return 0; -} - - -/* add playback controls from the parsed DAC table */ -static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; - - if (!cfg->line_out_pins[0]) - return -1; - - /* Line-Out: PortE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x9; - spec->hp_independent_mode_index = 1; - - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL( - spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} static int vt1812_parse_auto_config(struct hda_codec *codec) { @@ -5853,17 +4847,17 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; fill_dig_outs(codec); - err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); + err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) return 0; /* can't find valid BIOS pin config */ - err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); -- cgit v1.2.3 From f4a7828bc1e85b8de03b628da1cef4e862e0623b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Jun 2011 18:46:48 +0200 Subject: ALSA: hda - Re-implement smart51 detection for VIA codecs Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 117 +++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 42 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 1a3618599dec..77ecc778d313 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -162,6 +162,7 @@ struct via_spec { const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; unsigned int hp_independent_mode_index; + unsigned int can_smart51; unsigned int smart51_enabled; unsigned int dmic_enabled; unsigned int no_pin_power_ctl; @@ -544,7 +545,7 @@ static void via_auto_init_hp_out(struct hda_codec *codec) } } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); static void via_auto_init_analog_input(struct hda_codec *codec) { @@ -555,7 +556,7 @@ static void via_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - if (spec->smart51_enabled && is_smart51_pins(spec, nid)) + if (spec->smart51_enabled && is_smart51_pins(codec, nid)) ctl = PIN_OUT; else if (cfg->inputs[i].type == AUTO_PIN_MIC) ctl = PIN_VREF50; @@ -580,7 +581,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, no_presence |= spec->no_pin_power_ctl; if (!no_presence) present = snd_hda_jack_detect(codec, nid); - if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) + if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ @@ -919,16 +920,25 @@ static void mute_aa_path(struct hda_codec *codec, int mute) HDA_AMP_MUTE, val); } } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) { + struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; int i; for (i = 0; i < cfg->num_inputs; i++) { - if (pin == cfg->inputs[i].pin) - return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; + unsigned int defcfg; + if (pin != cfg->inputs[i].pin) + continue; + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) + return false; + defcfg = snd_hda_codec_get_pincfg(codec, pin); + if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) + return false; + return true; } - return 0; + return false; } static int via_smart51_info(struct snd_kcontrol *kcontrol, @@ -952,13 +962,14 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol, for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - int ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - continue; + unsigned int ctl; if (cfg->inputs[i].type == AUTO_PIN_MIC && spec->hp_independent_mode && spec->codec_type != VT1718S) continue; /* ignore FMic for independent HP */ + if (!is_smart51_pins(codec, nid)) + continue; + ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) on = 0; } @@ -980,11 +991,11 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = cfg->inputs[i].pin; unsigned int parm; - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - continue; if (cfg->inputs[i].type == AUTO_PIN_MIC && spec->hp_independent_mode && spec->codec_type != VT1718S) continue; /* don't retask FMic for independent HP */ + if (!is_smart51_pins(codec, nid)) + continue; parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -997,23 +1008,6 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, mute_aa_path(codec, 1); notify_aa_path_ctls(codec); } - if (spec->codec_type == VT1718S) { - snd_hda_codec_amp_stereo( - codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, - HDA_AMP_UNMUTE); - } - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - if (spec->codec_type == VT1708S - || spec->codec_type == VT1716S) { - /* input = index 1 (AOW3) */ - snd_hda_codec_write( - codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, 1); - snd_hda_codec_amp_stereo( - codec, nid, HDA_OUTPUT, - 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); - } - } } spec->smart51_enabled = *ucontrol->value.integer.value; set_widgets_power_state(codec); @@ -1029,16 +1023,15 @@ static const struct snd_kcontrol_new via_smart51_mixer = { .put = via_smart51_put, }; -static int via_smart51_build(struct via_spec *spec) +static int via_smart51_build(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; struct snd_kcontrol_new *knew; const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; int i; - if (!cfg) - return 0; - if (cfg->line_outs > 2) + if (!spec->can_smart51) return 0; knew = via_clone_control(spec, &via_smart51_mixer); @@ -1047,7 +1040,7 @@ static int via_smart51_build(struct via_spec *spec) for (i = 0; i < cfg->num_inputs; i++) { nid = cfg->inputs[i].pin; - if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { + if (is_smart51_pins(codec, nid)) { knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; break; } @@ -1943,15 +1936,37 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid); +static void mangle_smart51(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + if (!is_smart51_pins(codec, cfg->inputs[i].pin)) + continue; + spec->can_smart51 = 1; + cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; + if (cfg->line_outs == 3) + break; + } +} + /* add playback controls from the parsed DAC table */ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *cfg = &spec->autocfg; static const char * const chname[4] = { "Front", "Surround", "C/LFE", "Side" }; int i, idx, err; + int old_line_outs; + + /* check smart51 */ + old_line_outs = cfg->line_outs; + if (cfg->line_outs == 1) + mangle_smart51(codec); for (i = 0; i < cfg->line_outs; i++) { hda_nid_t pin, dac; @@ -1991,6 +2006,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) return err; } + cfg->line_outs = old_line_outs; + return 0; } @@ -2246,7 +2263,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) via_hp_build(codec); - via_smart51_build(spec); + err = via_smart51_build(codec); + if (err < 0) + return err; + return 1; } @@ -2523,7 +2543,10 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) via_hp_build(codec); - via_smart51_build(spec); + err = via_smart51_build(codec); + if (err < 0) + return err; + return 1; } @@ -2884,7 +2907,10 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) via_hp_build(codec); - via_smart51_build(spec); + err = via_smart51_build(codec); + if (err < 0) + return err; + return 1; } @@ -3267,7 +3293,10 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) via_hp_build(codec); - via_smart51_build(spec); + err = via_smart51_build(codec); + if (err < 0) + return err; + return 1; } @@ -3775,7 +3804,9 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) via_hp_build(codec); - via_smart51_build(spec); + err = via_smart51_build(codec); + if (err < 0) + return err; return 1; } @@ -4127,7 +4158,9 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) via_hp_build(codec); - via_smart51_build(spec); + err = via_smart51_build(codec); + if (err < 0) + return err; return 1; } -- cgit v1.2.3 From 57307bf24ac78d135c066520234c01bda36ec39a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 18 Jun 2011 10:58:49 +0200 Subject: ALSA: hda - Don't create secondary substream when no independent-hp is used For VIA codecs, we shouldn't create a substream for independent HP mode, when no individual HP DAC is found. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 77ecc778d313..78e679e76ca8 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1557,6 +1557,8 @@ static int via_build_pcms(struct hda_codec *codec) *(spec->stream_analog_playback); info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + if (!spec->multiout.hp_nid) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; -- cgit v1.2.3 From 9af7421091fd37a2f8c35ca8b3a5f78a6f20fa89 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 18 Jun 2011 16:17:45 +0200 Subject: ALSA: hda - Unify PCM assignments in patch_via.c Unify PCM streams for all codecs by assigning the NID dynamically. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 500 +++++----------------------------------------- 1 file changed, 46 insertions(+), 454 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 78e679e76ca8..18f2a135c026 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1222,6 +1222,17 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, hinfo); } +static int via_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + + analog_low_current_mode(codec, idle); + return 0; +} + static void playback_multi_pcm_prep_0(struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -1419,23 +1430,24 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } -static const struct hda_pcm_stream vt1708_pcm_analog_playback = { - .substreams = 2, +static const struct hda_pcm_stream via_pcm_analog_playback = { + .substreams = 2, /* will be changed in via_build_pcms() */ .channels_min = 2, .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ + /* NID is set in via_build_pcms */ .ops = { .open = via_playback_pcm_open, + .close = via_playback_pcm_close, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup }, }; static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 2, + .substreams = 2, /* will be changed in via_build_pcms() */ .channels_min = 2, .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ + /* NID is set in via_build_pcms */ /* We got noisy outputs on the right channel on VT1708 when * 24bit samples are used. Until any workaround is found, * disable the 24bit format, so far. @@ -1443,23 +1455,26 @@ static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { .open = via_playback_pcm_open, + .close = via_playback_pcm_close, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup }, }; -static const struct hda_pcm_stream vt1708_pcm_analog_capture = { - .substreams = 2, +static const struct hda_pcm_stream via_pcm_analog_capture = { + .substreams = 2, /* will be changed in via_build_pcms() */ .channels_min = 2, .channels_max = 2, - .nid = 0x15, /* NID to query formats and rates */ + /* NID is set in via_build_pcms */ .ops = { + .open = via_playback_pcm_open, + .close = via_playback_pcm_close, .prepare = via_capture_pcm_prepare, .cleanup = via_capture_pcm_cleanup }, }; -static const struct hda_pcm_stream vt1708_pcm_digital_playback = { +static const struct hda_pcm_stream via_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -1472,7 +1487,7 @@ static const struct hda_pcm_stream vt1708_pcm_digital_playback = { }, }; -static const struct hda_pcm_stream vt1708_pcm_digital_capture = { +static const struct hda_pcm_stream via_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -1553,17 +1568,25 @@ static int via_build_pcms(struct hda_codec *codec) snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; + + if (!spec->stream_analog_playback) + spec->stream_analog_playback = &via_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *(spec->stream_analog_playback); + *spec->stream_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; if (!spec->multiout.hp_nid) info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams = 1; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; + if (!spec->stream_analog_capture) + spec->stream_analog_capture = &via_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids; if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; @@ -1574,14 +1597,20 @@ static int via_build_pcms(struct hda_codec *codec) info->name = spec->stream_name_digital; info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { + if (!spec->stream_digital_playback) + spec->stream_digital_playback = + &via_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *(spec->stream_digital_playback); + *spec->stream_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; } if (spec->dig_in_nid) { + if (!spec->stream_digital_capture) + spec->stream_digital_capture = + &via_pcm_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *(spec->stream_digital_capture); + *spec->stream_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } @@ -2357,14 +2386,9 @@ static int patch_vt1708(struct hda_codec *codec) } - spec->stream_analog_playback = &vt1708_pcm_analog_playback; /* disable 32bit format on VT1708 */ if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; - spec->stream_analog_capture = &vt1708_pcm_analog_capture; - - spec->stream_digital_playback = &vt1708_pcm_digital_playback; - spec->stream_digital_capture = &vt1708_pcm_digital_capture; if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1708_capture_mixer; @@ -2453,58 +2477,6 @@ static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 10, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream vt1709_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x14, /* NID to query formats and rates */ - .ops = { - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1709_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close - }, -}; - -static const struct hda_pcm_stream vt1709_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - static int vt1709_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2586,12 +2558,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1709_pcm_analog_capture; - - spec->stream_digital_playback = &vt1709_pcm_digital_playback; - spec->stream_digital_capture = &vt1709_pcm_digital_capture; - if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1709_capture_mixer; spec->num_mixers++; @@ -2674,12 +2640,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1709_pcm_analog_capture; - - spec->stream_digital_playback = &vt1709_pcm_digital_playback; - spec->stream_digital_capture = &vt1709_pcm_digital_capture; - if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1709_capture_mixer; spec->num_mixers++; @@ -2801,74 +2761,6 @@ static const struct hda_verb vt1708B_uniwill_init_verbs[] = { { } }; -static int via_pcm_open_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - - analog_low_current_mode(codec, idle); - return 0; -} - -static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 4, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1708B_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708B_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1708B_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - static int vt1708B_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3034,12 +2926,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - - spec->stream_digital_playback = &vt1708B_pcm_digital_playback; - spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; spec->num_mixers++; @@ -3081,12 +2967,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - - spec->stream_digital_playback = &vt1708B_pcm_digital_playback; - spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; spec->num_mixers++; @@ -3182,58 +3062,6 @@ static const struct hda_verb vt1705_uniwill_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt1708S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1705_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708S_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - /* fill out digital output widgets; one for master and one for slave outputs */ static void fill_dig_outs(struct hda_codec *codec) { @@ -3352,14 +3180,6 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - if (codec->vendor_id == 0x11064397) - spec->stream_analog_playback = &vt1705_pcm_analog_playback; - else - spec->stream_analog_playback = &vt1708S_pcm_analog_playback; - spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - - spec->stream_digital_playback = &vt1708S_pcm_digital_playback; - if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -3463,45 +3283,6 @@ static const struct hda_verb vt1702_uniwill_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt1702_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1702_pcm_analog_capture = { - .substreams = 3, - .channels_min = 2, - .channels_max = 2, - .nid = 0x12, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1702_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - static int vt1702_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3613,11 +3394,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; - spec->stream_analog_playback = &vt1702_pcm_analog_playback; - spec->stream_analog_capture = &vt1702_pcm_analog_capture; - - spec->stream_digital_playback = &vt1702_pcm_digital_playback; - if (spec->adc_nids && spec->input_mux) { spec->mixers[spec->num_mixers] = vt1702_capture_mixer; spec->num_mixers++; @@ -3721,51 +3497,6 @@ static const struct hda_verb vt1718S_uniwill_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt1718S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 10, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1718S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1718S_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1718S_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - static int vt1718S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3914,13 +3645,6 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - spec->stream_analog_playback = &vt1718S_pcm_analog_playback; - spec->stream_analog_capture = &vt1718S_pcm_analog_capture; - - spec->stream_digital_playback = &vt1718S_pcm_digital_playback; - if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) - spec->stream_digital_capture = &vt1718S_pcm_digital_capture; - if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -4085,45 +3809,6 @@ static const struct hda_verb vt1716S_uniwill_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt1716S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1716S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1716S_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - static int vt1716S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4295,11 +3980,6 @@ static int patch_vt1716S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; - spec->stream_analog_playback = &vt1716S_pcm_analog_playback; - spec->stream_analog_capture = &vt1716S_pcm_analog_capture; - - spec->stream_digital_playback = &vt1716S_pcm_digital_playback; - if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -4480,45 +4160,6 @@ static const struct hda_verb vt1802_uniwill_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt2002P_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt2002P_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt2002P_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - static int vt2002P_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4718,11 +4359,6 @@ static int patch_vt2002P(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; - spec->stream_analog_playback = &vt2002P_pcm_analog_playback; - spec->stream_analog_capture = &vt2002P_pcm_analog_capture; - - spec->stream_digital_playback = &vt2002P_pcm_digital_playback; - if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -4833,45 +4469,6 @@ static const struct hda_verb vt1812_uniwill_init_verbs[] = { { } }; -static const struct hda_pcm_stream vt1812_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1812_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1812_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - static int vt1812_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5042,11 +4639,6 @@ static int patch_vt1812(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; - spec->stream_analog_playback = &vt1812_pcm_analog_playback; - spec->stream_analog_capture = &vt1812_pcm_analog_capture; - - spec->stream_digital_playback = &vt1812_pcm_digital_playback; - if (spec->adc_nids && spec->input_mux) { override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); -- cgit v1.2.3 From 7eb56e84e6c4deaa552db96834ea0b233ba92f50 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 18 Jun 2011 16:40:14 +0200 Subject: ALSA: hda - Assign HP-independent PCM to individual stream Instead of using the secondary substream, create an individual PCM stream for HP-independent PCM. Otherwise it's difficult to handle different channel numbers with multi-channel stream in the sam PCM stream structure. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 144 +++++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 18f2a135c026..264889c9c177 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -122,6 +122,7 @@ struct via_spec { unsigned int num_iverbs; char stream_name_analog[32]; + char stream_name_hp[32]; const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; @@ -1210,14 +1211,20 @@ static const struct hda_verb vt1708_volume_init_verbs[] = { { } }; +static void substream_set_idle(struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + analog_low_current_mode(codec, idle); +} + static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - analog_low_current_mode(codec, idle); + substream_set_idle(codec, substream); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); } @@ -1226,17 +1233,29 @@ static int via_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; + substream_set_idle(codec, substream); + return 0; +} - analog_low_current_mode(codec, idle); +static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + struct hda_multi_out *mout = &spec->multiout; + + if (!mout->hp_nid || mout->hp_nid == mout->dac_nids[HDA_FRONT] || + !spec->hp_independent_mode) + return -EINVAL; + substream_set_idle(codec, substream); return 0; } -static void playback_multi_pcm_prep_0(struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; struct hda_multi_out *mout = &spec->multiout; @@ -1301,27 +1320,20 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); } + vt1708_start_hp_work(spec); + return 0; } -static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; struct hda_multi_out *mout = &spec->multiout; - const hda_nid_t *nids = mout->dac_nids; - if (substream->number == 0) - playback_multi_pcm_prep_0(codec, stream_tag, format, - substream); - else { - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - stream_tag, 0, format); - } + snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); vt1708_start_hp_work(spec); return 0; } @@ -1335,33 +1347,38 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, const hda_nid_t *nids = mout->dac_nids; int i; - if (substream->number == 0) { - for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); + for (i = 0; i < mout->num_dacs; i++) + snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); - if (mout->hp_nid && !spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - 0, 0, 0); + if (mout->hp_nid && !spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, mout->hp_nid, + 0, 0, 0); - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - 0, 0, 0); - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && - mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - mout->dig_out_used = 0; - } - mutex_unlock(&codec->spdif_mutex); - } else { - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, + for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) + if (mout->extra_out_nid[i]) + snd_hda_codec_setup_stream(codec, + mout->extra_out_nid[i], 0, 0, 0); + mutex_lock(&codec->spdif_mutex); + if (mout->dig_out_nid && + mout->dig_out_used == HDA_DIG_ANALOG_DUP) { + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, + 0, 0, 0); + mout->dig_out_used = 0; } + mutex_unlock(&codec->spdif_mutex); + vt1708_stop_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + struct hda_multi_out *mout = &spec->multiout; + + snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); vt1708_stop_hp_work(spec); return 0; } @@ -1431,7 +1448,7 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, } static const struct hda_pcm_stream via_pcm_analog_playback = { - .substreams = 2, /* will be changed in via_build_pcms() */ + .substreams = 1, .channels_min = 2, .channels_max = 8, /* NID is set in via_build_pcms */ @@ -1443,8 +1460,21 @@ static const struct hda_pcm_stream via_pcm_analog_playback = { }, }; +static const struct hda_pcm_stream via_pcm_hp_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_playback_hp_pcm_open, + .close = via_playback_pcm_close, + .prepare = via_playback_hp_pcm_prepare, + .cleanup = via_playback_hp_pcm_cleanup + }, +}; + static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 2, /* will be changed in via_build_pcms() */ + .substreams = 1, .channels_min = 2, .channels_max = 8, /* NID is set in via_build_pcms */ @@ -1462,7 +1492,7 @@ static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { }; static const struct hda_pcm_stream via_pcm_analog_capture = { - .substreams = 2, /* will be changed in via_build_pcms() */ + .substreams = 1, /* will be changed in via_build_pcms() */ .channels_min = 2, .channels_max = 2, /* NID is set in via_build_pcms */ @@ -1577,8 +1607,6 @@ static int via_build_pcms(struct hda_codec *codec) spec->multiout.dac_nids[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; - if (!spec->multiout.hp_nid) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams = 1; if (!spec->stream_analog_capture) spec->stream_analog_capture = &via_pcm_analog_capture; @@ -1616,6 +1644,16 @@ static int via_build_pcms(struct hda_codec *codec) } } + if (spec->multiout.hp_nid) { + codec->num_pcms++; + info++; + snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), + "%s HP", codec->chip_name); + info->name = spec->stream_name_hp; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.hp_nid; + } return 0; } -- cgit v1.2.3 From d7a99cce558f84477adacce9324ab22f52c951ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 18 Jun 2011 17:24:46 +0200 Subject: ALSA: hda - Unify capture-mixer creations in patch_via.c Create capture-related mixer elements dynamically from the parsed ADCs and input-pins instead of fixed values for each codec. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 341 ++++++++++------------------------------------ 1 file changed, 73 insertions(+), 268 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 264889c9c177..d64560f63a75 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -462,6 +462,7 @@ static int __via_add_control(struct via_spec *spec, int type, const char *name, knew = __via_clone_ctl(spec, &via_control_templates[type], name); if (!knew) return -ENOMEM; + knew->index = idx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; @@ -1050,27 +1051,6 @@ static int via_smart51_build(struct hda_codec *codec) return 0; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1708_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 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 = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - /* check AA path's mute statue */ static int is_aa_path_mute(struct hda_codec *codec) { @@ -2157,6 +2137,18 @@ static int via_fill_adcs(struct hda_codec *codec) static int get_mux_nids(struct hda_codec *codec); +static const struct snd_kcontrol_new via_input_src_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, +}; + /* create playback/capture controls for input pins */ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) @@ -2211,6 +2203,56 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, return err; snd_hda_add_imux_item(imux, label, idx, NULL); } + + /* create capture mixer elements */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t adc = spec->adc_nids[i]; + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Capture Volume", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Capture Switch", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + } + + /* input-source control */ + for (i = 0; i < spec->num_adc_nids; i++) + if (!spec->mux_nids[i]) + break; + if (i) { + struct snd_kcontrol_new *knew; + knew = via_clone_control(spec, &via_input_src_ctl); + if (!knew) + return -ENOMEM; + knew->count = i; + } + + /* mic-boosts */ + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int caps; + const char *label; + char name[32]; + + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + caps = query_amp_caps(codec, pin, HDA_INPUT); + if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + snprintf(name, sizeof(name), "%s Boost Capture Volume", label); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; } @@ -2428,11 +2470,6 @@ static int patch_vt1708(struct hda_codec *codec) if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; - if (spec->adc_nids && spec->input_mux) { - spec->mixers[spec->num_mixers] = vt1708_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -2443,29 +2480,6 @@ static int patch_vt1708(struct hda_codec *codec) return 0; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1709_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 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 = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct hda_verb vt1709_uniwill_init_verbs[] = { {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, @@ -2596,11 +2610,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - spec->mixers[spec->num_mixers] = vt1709_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -2678,11 +2687,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - spec->mixers[spec->num_mixers] = vt1709_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -2693,26 +2697,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) return 0; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 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 = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; /* * generic initialization of ADC, input mixers and output mixers */ @@ -2964,11 +2948,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -3005,11 +2984,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -3025,30 +2999,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) /* Patch for VT1708S */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 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 = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct hda_verb vt1708S_volume_init_verbs[] = { /* Unmute ADC0-1 and set the default input to mic-in */ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -3199,6 +3149,8 @@ static int patch_vt1708S(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x16; + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); /* automatic parse from the BIOS config */ err = vt1708S_parse_auto_config(codec); @@ -3218,13 +3170,6 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -3255,31 +3200,6 @@ static int patch_vt1708S(struct hda_codec *codec) /* Patch for VT1702 */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1702_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 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 = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct hda_verb vt1702_volume_init_verbs[] = { /* * Unmute ADC0-1 and set the default input to mic-in @@ -3432,11 +3352,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - spec->mixers[spec->num_mixers] = vt1702_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -3451,29 +3366,6 @@ static int patch_vt1702(struct hda_codec *codec) /* Patch for VT1718S */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct hda_verb vt1718S_volume_init_verbs[] = { /* * Unmute ADC0-1 and set the default input to mic-in @@ -3669,6 +3561,8 @@ static int patch_vt1718S(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); /* automatic parse from the BIOS config */ err = vt1718S_parse_auto_config(codec); @@ -3683,13 +3577,6 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -3744,26 +3631,6 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, return 1; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), { @@ -4004,6 +3871,8 @@ static int patch_vt1716S(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x16; + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); /* automatic parse from the BIOS config */ err = vt1716S_parse_auto_config(codec); @@ -4018,13 +3887,6 @@ static int patch_vt1716S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; - spec->num_mixers++; - } - spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; spec->num_mixers++; @@ -4045,30 +3907,6 @@ static int patch_vt1716S(struct hda_codec *codec) /* for vt2002P */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 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 = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct hda_verb vt2002P_volume_init_verbs[] = { /* Class-D speaker related verbs */ {0x1, 0xfe0, 0x4}, @@ -4372,6 +4210,8 @@ static int patch_vt2002P(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); /* automatic parse from the BIOS config */ err = vt2002P_parse_auto_config(codec); @@ -4397,13 +4237,6 @@ static int patch_vt2002P(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; @@ -4419,29 +4252,6 @@ static int patch_vt2002P(struct hda_codec *codec) /* for vt1812 */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1812_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct hda_verb vt1812_volume_init_verbs[] = { /* * Unmute ADC0-1 and set the default input to mic-in @@ -4662,6 +4472,8 @@ static int patch_vt1812(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); /* automatic parse from the BIOS config */ err = vt1812_parse_auto_config(codec); @@ -4677,13 +4489,6 @@ static int patch_vt1812(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; - if (spec->adc_nids && spec->input_mux) { - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1812_capture_mixer; - spec->num_mixers++; - } - codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; -- cgit v1.2.3 From 7f0df88ce0ad846b68156b73b3acc9f3901989d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 18 Jun 2011 17:33:34 +0200 Subject: ALSA: hda - Return error for invalid setup for VIA Instead of ignoring the invalid pin configuration, return the error. This will avoid unexpected crash, anyway. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 53 ++++++++--------------------------------------- 1 file changed, 9 insertions(+), 44 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d64560f63a75..f91c4db038e4 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2341,7 +2341,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -2460,12 +2460,8 @@ static int patch_vt1708(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - /* disable 32bit format on VT1708 */ if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; @@ -2541,7 +2537,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -2602,9 +2598,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration. " - "Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; @@ -2679,9 +2672,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration. " - "Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; @@ -2795,7 +2785,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -2940,9 +2930,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; @@ -2976,9 +2963,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; @@ -3087,7 +3071,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -3157,9 +3141,6 @@ static int patch_vt1708S(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; @@ -3253,7 +3234,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -3344,9 +3325,6 @@ static int patch_vt1702(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; @@ -3440,7 +3418,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -3569,9 +3547,6 @@ static int patch_vt1718S(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; @@ -3726,7 +3701,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -3879,9 +3854,6 @@ static int patch_vt1716S(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; @@ -4051,7 +4023,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -4218,9 +4190,6 @@ static int patch_vt2002P(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } if (spec->codec_type == VT1802) @@ -4332,7 +4301,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; err = via_auto_create_multi_out_ctls(codec); if (err < 0) @@ -4480,12 +4449,8 @@ static int patch_vt1812(struct hda_codec *codec) if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; -- cgit v1.2.3 From 12daef65fd868cf30be5afe3e6be6689c44c7940 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 18 Jun 2011 17:45:49 +0200 Subject: ALSA: hda - Unify auto-parser in patch_via.c Now all codecs use the same parser-path, so we can reduce into a single auto-parser function. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 427 ++++++---------------------------------------- 1 file changed, 53 insertions(+), 374 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index f91c4db038e4..14fccdc21c33 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2325,15 +2325,14 @@ static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { .put = vt1708_jack_detect_put, }; -static int vt1708_parse_auto_config(struct hda_codec *codec) +static void fill_dig_outs(struct hda_codec *codec); +static void fill_dig_in(struct hda_codec *codec); + +static int via_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int err; - /* Add HP and CD pin config connect bit re-config action */ - vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); - vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; @@ -2352,17 +2351,11 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; - /* add jack detect on/off control */ - if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) - return -ENOMEM; spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; - spec->dig_in_pin = VT1708_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1708_DIGIN_NID; + fill_dig_outs(codec); + fill_dig_in(codec); if (spec->kctls.list) spec->mixers[spec->num_mixers++] = spec->kctls.list; @@ -2455,13 +2448,21 @@ static int patch_vt1708(struct hda_codec *codec) spec->aa_mix_nid = 0x17; + /* Add HP and CD pin config connect bit re-config action */ + vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); + vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); + /* automatic parse from the BIOS config */ - err = vt1708_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; } + /* add jack detect on/off control */ + if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) + return -ENOMEM; + /* disable 32bit format on VT1708 */ if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; @@ -2525,53 +2526,6 @@ static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { { } }; -static int vt1709_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; - spec->dig_in_pin = VT1709_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1709_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - err = via_smart51_build(codec); - if (err < 0) - return err; - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1709_loopbacks[] = { { 0x18, HDA_INPUT, 1 }, @@ -2594,7 +2548,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->aa_mix_nid = 0x18; - err = vt1709_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -2668,7 +2622,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->aa_mix_nid = 0x18; - err = vt1709_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -2773,53 +2727,6 @@ static const struct hda_verb vt1708B_uniwill_init_verbs[] = { { } }; -static int vt1708B_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; - spec->dig_in_pin = VT1708B_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1708B_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - err = via_smart51_build(codec); - if (err < 0) - return err; - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1708B_loopbacks[] = { { 0x16, HDA_INPUT, 1 }, @@ -2926,7 +2833,7 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) spec->aa_mix_nid = 0x16; /* automatic parse from the BIOS config */ - err = vt1708B_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -2959,7 +2866,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) return -ENOMEM; /* automatic parse from the BIOS config */ - err = vt1708B_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -3059,47 +2966,31 @@ static void fill_dig_outs(struct hda_codec *codec) } } -static int vt1708S_parse_auto_config(struct hda_codec *codec) +static void fill_dig_in(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; + hda_nid_t dig_nid; + int i, err; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - err = via_smart51_build(codec); - if (err < 0) - return err; + if (!spec->autocfg.dig_in_pin) + return; - return 1; + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + err = get_connection_index(codec, dig_nid, + spec->autocfg.dig_in_pin); + if (err >= 0) { + spec->dig_in_nid = dig_nid; + break; + } + } } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -3137,7 +3028,7 @@ static int patch_vt1708S(struct hda_codec *codec) override_mic_boost(codec, 0x1e, 0, 3, 40); /* automatic parse from the BIOS config */ - err = vt1708S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -3222,51 +3113,6 @@ static const struct hda_verb vt1702_uniwill_init_verbs[] = { { } }; -static int vt1702_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - /* limit AA path volume to 0 dB */ - snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1702_loopbacks[] = { { 0x1A, HDA_INPUT, 1 }, @@ -3320,8 +3166,15 @@ static int patch_vt1702(struct hda_codec *codec) spec->aa_mix_nid = 0x1a; + /* limit AA path volume to 0 dB */ + snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + /* automatic parse from the BIOS config */ - err = vt1702_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -3405,53 +3258,6 @@ static const struct hda_verb vt1718S_uniwill_init_verbs[] = { { } }; -static int vt1718S_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) - spec->dig_in_nid = 0x13; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - err = via_smart51_build(codec); - if (err < 0) - return err; - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1718S_loopbacks[] = { { 0x21, HDA_INPUT, 1 }, @@ -3543,7 +3349,7 @@ static int patch_vt1718S(struct hda_codec *codec) override_mic_boost(codec, 0x29, 0, 3, 40); /* automatic parse from the BIOS config */ - err = vt1718S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -3689,49 +3495,6 @@ static const struct hda_verb vt1716S_uniwill_init_verbs[] = { { } }; -static int vt1716S_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - err = via_smart51_build(codec); - if (err < 0) - return err; - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1716S_loopbacks[] = { { 0x16, HDA_INPUT, 1 }, @@ -3850,7 +3613,7 @@ static int patch_vt1716S(struct hda_codec *codec) override_mic_boost(codec, 0x1e, 0, 3, 40); /* automatic parse from the BIOS config */ - err = vt1716S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -4008,48 +3771,6 @@ static const struct hda_verb vt1802_uniwill_init_verbs[] = { { } }; -static int vt2002P_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt2002P_loopbacks[] = { { 0x21, HDA_INPUT, 0 }, @@ -4186,7 +3907,7 @@ static int patch_vt2002P(struct hda_codec *codec) override_mic_boost(codec, 0x29, 0, 3, 40); /* automatic parse from the BIOS config */ - err = vt2002P_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; @@ -4286,48 +4007,6 @@ static const struct hda_verb vt1812_uniwill_init_verbs[] = { { } }; -static int vt1812_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - fill_dig_outs(codec); - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1812_loopbacks[] = { { 0x21, HDA_INPUT, 0 }, @@ -4445,7 +4124,7 @@ static int patch_vt1812(struct hda_codec *codec) override_mic_boost(codec, 0x29, 0, 3, 40); /* automatic parse from the BIOS config */ - err = vt1812_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; -- cgit v1.2.3 From ece8d0431fde78ea2c0a5be2884bcbc4940ae7c5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 19 Jun 2011 16:24:21 +0200 Subject: ALSA: hda - Fix independent-HP handling in patch_via.c Fix races in handling of HP DAC and independent streams for VIA codecs. Also, allow the HP output path without front-DAC, and removed unnecessary activation of HP mixer elements. This also removes the handling of shared side/HP stream; it's anyway implemented in a broken way, so we need to re-implement the feature later... Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 296 +++++++++------------------------------------- 1 file changed, 58 insertions(+), 238 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 14fccdc21c33..fa5ed36d69e5 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -133,6 +133,7 @@ struct via_spec { /* playback */ struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; + hda_nid_t hp_dac_nid; struct nid_path out_path[4]; struct nid_path hp_path; @@ -702,64 +703,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int pinsel; - - /* use !! to translate conn sel 2 for VT1718S */ - pinsel = !!snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, - 0x00); - ucontrol->value.enumerated.item[0] = pinsel; - - return 0; -} - -static void activate_ctl(struct hda_codec *codec, const char *name, int active) -{ - struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); - if (ctl) { - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access |= active - ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); - } -} - -static hda_nid_t side_mute_channel(struct via_spec *spec) -{ - switch (spec->codec_type) { - case VT1708: return 0x1b; - case VT1709_10CH: return 0x29; - case VT1708B_8CH: /* fall thru */ - case VT1708S: return 0x27; - case VT2002P: return 0x19; - case VT1802: return 0x15; - case VT1812: return 0x15; - default: return 0; - } -} - -static int update_side_mute_status(struct hda_codec *codec) -{ - /* mute side channel */ struct via_spec *spec = codec->spec; - unsigned int parm; - hda_nid_t sw3 = side_mute_channel(spec); - if (sw3) { - if (VT2002P_COMPATIBLE(spec)) - parm = spec->hp_independent_mode ? - AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); - else - parm = spec->hp_independent_mode ? - AMP_OUT_MUTE : AMP_OUT_UNMUTE; - snd_hda_codec_write(codec, sw3, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm); - if (spec->codec_type == VT1812) - snd_hda_codec_write(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm); - } + ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; return 0; } @@ -773,50 +719,19 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, /* Get Independent Mode index of headphone pin widget */ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; - if (spec->codec_type == VT1718S) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); - else - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); - if (spec->codec_type == VT1812) - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); - if (spec->multiout.hp_nid && spec->multiout.hp_nid - != spec->multiout.dac_nids[HDA_FRONT]) - snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, - 0, 0, 0); - - update_side_mute_status(codec); - /* update HP volume/swtich active state */ - if (spec->codec_type == VT1708S - || spec->codec_type == VT1702 - || spec->codec_type == VT1718S - || spec->codec_type == VT1716S - || VT2002P_COMPATIBLE(spec)) { - activate_ctl(codec, "Headphone Playback Volume", - spec->hp_independent_mode); - activate_ctl(codec, "Headphone Playback Switch", - spec->hp_independent_mode); - } /* update jack power state */ set_widgets_power_state(codec); return 0; } -static const struct snd_kcontrol_new via_hp_mixer[2] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = via_independent_hp_info, - .get = via_independent_hp_get, - .put = via_independent_hp_put, - }, - { - .iface = NID_MAPPING, - .name = "Independent HP", - }, +static const struct snd_kcontrol_new via_hp_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = via_independent_hp_info, + .get = via_independent_hp_get, + .put = via_independent_hp_put, }; static int via_hp_build(struct hda_codec *codec) @@ -824,44 +739,15 @@ static int via_hp_build(struct hda_codec *codec) struct via_spec *spec = codec->spec; struct snd_kcontrol_new *knew; hda_nid_t nid; - int nums; - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - break; - case VT2002P: - case VT1802: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } - - if (spec->codec_type != VT1708) { - nums = snd_hda_get_connections(codec, nid, - conn, HDA_MAX_CONNECTIONS); - if (nums <= 1) - return 0; - } - - knew = via_clone_control(spec, &via_hp_mixer[0]); + nid = spec->autocfg.hp_pins[0]; + knew = via_clone_control(spec, &via_hp_mixer); if (knew == NULL) return -ENOMEM; knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; knew->private_value = nid; - knew = via_clone_control(spec, &via_hp_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = side_mute_channel(spec); - return 0; } @@ -1199,20 +1085,26 @@ static void substream_set_idle(struct hda_codec *codec, analog_low_current_mode(codec, idle); } -static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, +static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + + if (!spec->hp_independent_mode) + spec->multiout.hp_nid = spec->hp_dac_nid; substream_set_idle(codec, substream); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); } -static int via_playback_pcm_close(struct hda_pcm_stream *hinfo, +static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { + struct via_spec *spec = codec->spec; + + spec->multiout.hp_nid = 0; substream_set_idle(codec, substream); return 0; } @@ -1222,11 +1114,19 @@ static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - if (!mout->hp_nid || mout->hp_nid == mout->dac_nids[HDA_FRONT] || - !spec->hp_independent_mode) + if (snd_BUG_ON(!spec->hp_dac_nid)) return -EINVAL; + if (!spec->hp_independent_mode || spec->multiout.hp_nid) + return -EBUSY; + substream_set_idle(codec, substream); + return 0; +} + +static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ substream_set_idle(codec, substream); return 0; } @@ -1238,68 +1138,9 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - const hda_nid_t *nids = mout->dac_nids; - int chs = substream->runtime->channels; - int i; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); - - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { - if (chs == 2 && - snd_hda_is_supported_format(codec, mout->dig_out_nid, - format) && - !(spdif->status & IEC958_AES0_NONAUDIO)) { - mout->dig_out_used = HDA_DIG_ANALOG_DUP; - /* turn off SPDIF once; otherwise the IEC958 bits won't - * be updated */ - if (spdif->ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, mout->dig_out_nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & - ~AC_DIG1_ENABLE & 0xff); - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - stream_tag, 0, format); - /* turn on again (if needed) */ - if (spdif->ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, mout->dig_out_nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - } else { - mout->dig_out_used = 0; - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - } - } - mutex_unlock(&codec->spdif_mutex); - - /* front */ - snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, - 0, format); - - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] - && !spec->hp_independent_mode) - /* headphone out will just decode front left/right (stereo) */ - snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, - 0, format); - - /* extra outputs copied from front */ - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - stream_tag, 0, format); - - /* surrounds */ - for (i = 1; i < mout->num_dacs; i++) { - if (chs >= (i + 1) * 2) /* independent out */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - i * 2, format); - else /* copy front */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - 0, format); - } + + snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); vt1708_start_hp_work(spec); return 0; } @@ -1311,9 +1152,9 @@ static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, + stream_tag, 0, format); vt1708_start_hp_work(spec); return 0; } @@ -1323,30 +1164,8 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - const hda_nid_t *nids = mout->dac_nids; - int i; - for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); - - if (mout->hp_nid && !spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - 0, 0, 0); - - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - 0, 0, 0); - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && - mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - mout->dig_out_used = 0; - } - mutex_unlock(&codec->spdif_mutex); + snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); vt1708_stop_hp_work(spec); return 0; } @@ -1356,9 +1175,8 @@ static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); vt1708_stop_hp_work(spec); return 0; } @@ -1433,8 +1251,8 @@ static const struct hda_pcm_stream via_pcm_analog_playback = { .channels_max = 8, /* NID is set in via_build_pcms */ .ops = { - .open = via_playback_pcm_open, - .close = via_playback_pcm_close, + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup }, @@ -1447,7 +1265,7 @@ static const struct hda_pcm_stream via_pcm_hp_playback = { /* NID is set in via_build_pcms */ .ops = { .open = via_playback_hp_pcm_open, - .close = via_playback_pcm_close, + .close = via_playback_hp_pcm_close, .prepare = via_playback_hp_pcm_prepare, .cleanup = via_playback_hp_pcm_cleanup }, @@ -1464,8 +1282,8 @@ static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { */ .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { - .open = via_playback_pcm_open, - .close = via_playback_pcm_close, + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup }, @@ -1477,8 +1295,6 @@ static const struct hda_pcm_stream via_pcm_analog_capture = { .channels_max = 2, /* NID is set in via_build_pcms */ .ops = { - .open = via_playback_pcm_open, - .close = via_playback_pcm_close, .prepare = via_capture_pcm_prepare, .cleanup = via_capture_pcm_cleanup }, @@ -1624,7 +1440,7 @@ static int via_build_pcms(struct hda_codec *codec) } } - if (spec->multiout.hp_nid) { + if (spec->hp_dac_nid) { codec->num_pcms++; info++; snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), @@ -1632,7 +1448,7 @@ static int via_build_pcms(struct hda_codec *codec) info->name = spec->stream_name_hp; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.hp_nid; + spec->hp_dac_nid; } return 0; } @@ -1883,7 +1699,7 @@ static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) if (spec->multiout.dac_nids[i] == dac) return false; } - if (spec->multiout.hp_nid == dac) + if (spec->hp_dac_nid == dac) return false; return true; } @@ -2076,24 +1892,25 @@ static void create_hp_imux(struct via_spec *spec) static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; - hda_nid_t dac = 0; int err; if (!pin) return 0; - if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - &spec->hp_dep_path, 0, -1)) - return 0; if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { - dac = spec->hp_path.path[spec->hp_path.depth - 1]; - spec->multiout.hp_nid = dac; + spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1]; spec->hp_independent_mode_index = spec->hp_path.idx[spec->hp_path.depth - 1]; create_hp_imux(spec); } - err = create_ch_ctls(codec, "Headphone", pin, dac, 3); + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + &spec->hp_dep_path, 0, -1) && + !spec->hp_dac_nid) + return 0; + + + err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); if (err < 0) return err; @@ -2364,8 +2181,11 @@ static int via_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; - if (spec->hp_mux) - via_hp_build(codec); + if (spec->hp_mux) { + err = via_hp_build(codec); + if (err < 0) + return err; + } err = via_smart51_build(codec); if (err < 0) -- cgit v1.2.3 From 0fe0adf82f95ed5ce5a75512b281f6cbc89cefa1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 19 Jun 2011 16:27:53 +0200 Subject: ALSA: hda - Replace with standard consts in patch_via.c Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fa5ed36d69e5..ae90b95eab3a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -263,13 +263,6 @@ enum { VIA_CTL_WIDGET_BIND_PIN_MUTE, }; -enum { - AUTO_SEQ_FRONT = 0, - AUTO_SEQ_SURROUND, - AUTO_SEQ_CENLFE, - AUTO_SEQ_SIDE -}; - static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); static int is_aa_path_mute(struct hda_codec *codec); @@ -528,7 +521,7 @@ static void via_auto_init_multi_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; int i; - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); @@ -1839,7 +1832,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) dac = spec->multiout.dac_nids[i]; if (!pin || !dac) continue; - if (i == AUTO_SEQ_CENLFE) { + if (i == HDA_CLFE) { err = create_ch_ctls(codec, "Center", pin, dac, 1); if (err < 0) return err; -- cgit v1.2.3 From 5d41762a210851943f59f0a08656ca582f76d9d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 11:32:27 +0200 Subject: ALSA: hda - Initialize output path dynamically in patch_via.c Instead of fixed array for each codec type, initialize the output path dynamically from the parsed results. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 349 ++++++++++++++++++---------------------------- 1 file changed, 135 insertions(+), 214 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index ae90b95eab3a..4f6e7bebdb45 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -501,44 +501,126 @@ static int via_new_analog_input(struct via_spec *spec, const char *ctlname, return 0; } -static void via_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +/* return the index of the given widget nid as the source of mux; + * return -1 if not found; + * if num_conns is non-NULL, set the total number of connections + */ +static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid, int *num_conns) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + if (num_conns) + *num_conns = nums; + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + return -1; +} + +#define get_connection_index(codec, mux, nid) \ + __get_connection_index(codec, mux, nid, NULL) + +/* unmute input amp and select the specificed source */ +static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t src, hda_nid_t mix) +{ + int idx, num_conns; + + idx = __get_connection_index(codec, nid, src, &num_conns); + if (idx < 0) + return; + + /* select the route explicitly when multiple connections exist */ + if (num_conns > 1) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, idx); + /* unmute if the input amp is present */ + if (!(query_amp_caps(codec, nid, HDA_INPUT) & + (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))) + return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) + AMP_IN_UNMUTE(idx)); + + /* unmute AA-path if present */ + if (!mix) + return; + idx = __get_connection_index(codec, nid, mix, NULL); + if (idx >= 0) snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(idx)); +} + +/* set the given pin as output */ +static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, + int pin_type) +{ + if (!pin) + return; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_type); + if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } +static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, + int pin_type, struct nid_path *path) +{ + struct via_spec *spec = codec->spec; + unsigned int caps; + hda_nid_t nid; + int i; + + if (!pin) + return; + + init_output_pin(codec, pin, pin_type); + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + if (caps & AC_AMPCAP_MUTE) { + unsigned int val; + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE | val); + } + + /* initialize the output path */ + nid = pin; + for (i = 0; i < path->depth; i++) { + unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid); + nid = path->path[i]; + if (query_amp_caps(codec, nid, HDA_OUTPUT) & + (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } +} + static void via_auto_init_multi_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int i; - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); - } + for (i = 0; i < spec->autocfg.line_outs; i++) + via_auto_init_output(codec, spec->autocfg.line_out_pins[i], + PIN_OUT, &spec->out_path[i]); } static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - hda_nid_t pin; - int i; - for (i = 0; i < spec->autocfg.hp_outs; i++) { - pin = spec->autocfg.hp_pins[i]; - if (pin) /* connect to front */ - via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); - } + if (spec->hp_dac_nid) + via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, + &spec->hp_path); + else + via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, + &spec->hp_dep_path); } static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); @@ -1053,18 +1135,6 @@ static const struct hda_verb vt1708_volume_init_verbs[] = { {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - /* - * Set up output mixers (0x19 - 0x1b) - */ - /* set vol=0 to output mixers */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input MW0 to PW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* power down jack detect function */ {0x1, 0xf81, 0x1}, { } @@ -1624,33 +1694,6 @@ static void via_unsol_event(struct hda_codec *codec, via_hp_bind_automute(codec); } -static int via_init(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - for (i = 0; i < spec->num_iverbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - - /* Lydia Add for EAPD enable */ - if (!spec->dig_in_nid) { /* No Digital In connection */ - if (spec->dig_in_pin) { - snd_hda_codec_write(codec, spec->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - snd_hda_codec_write(codec, spec->dig_in_pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); - } - } else /* enable SPDIF-input pin */ - snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); - - /* assign slave outs */ - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; - - return 0; -} - #ifdef SND_HDA_NEEDS_RESUME static int via_suspend(struct hda_codec *codec, pm_message_t state) { @@ -1670,6 +1713,9 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) /* */ + +static int via_init(struct hda_codec *codec); + static const struct hda_codec_ops via_patch_ops = { .build_controls = via_build_controls, .build_pcms = via_build_pcms, @@ -1791,9 +1837,6 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, return 0; } -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid); - static void mangle_smart51(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1910,19 +1953,6 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) return 0; } -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - return -1; -} - /* look for ADCs */ static int via_fill_adcs(struct hda_codec *codec) { @@ -2184,18 +2214,44 @@ static int via_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + /* assign slave outs */ + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; + return 1; } -/* init callback for auto-configuration model -- overriding the default init */ -static int via_auto_init(struct hda_codec *codec) +static void via_auto_init_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + if (spec->multiout.dig_out_nid) + init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); + if (spec->slave_dig_outs[0]) + init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); +} + +static void via_auto_init_dig_in(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + if (!spec->dig_in_nid) + return; + snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); +} + +static int via_init(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_iverbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); - via_init(codec); via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_analog_input(codec); + via_auto_init_dig_outs(codec); + via_auto_init_dig_in(codec); if (VT2002P_COMPATIBLE(spec)) { via_hp_bind_automute(codec); @@ -2282,7 +2338,6 @@ static int patch_vt1708(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708_loopbacks; #endif @@ -2318,24 +2373,8 @@ static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - /* - * Set up output selector (0x1a, 0x1b, 0x29) - */ - /* set vol=0 to output mixers */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Unmute PW3 and PW4 - */ - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Set input of PW4 as MW0 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } }; @@ -2372,7 +2411,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1709_loopbacks; @@ -2446,7 +2484,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1709_loopbacks; @@ -2483,8 +2520,6 @@ static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Setup default input to PW4 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PW10 Input enable */ @@ -2510,18 +2545,6 @@ static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - /* - * Set up output mixers - */ - /* set vol=0 to output mixers */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* PW9 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PW10 Input enable */ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, { } @@ -2657,7 +2680,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708B_loopbacks; @@ -2690,7 +2712,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708B_loopbacks; @@ -2717,11 +2738,6 @@ static const struct hda_verb vt1708S_volume_init_verbs[] = { {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* PW9, PW10 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, /* don't bybass mixer */ @@ -2857,7 +2873,6 @@ static int patch_vt1708S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708S_loopbacks; @@ -2904,11 +2919,6 @@ static const struct hda_verb vt1702_volume_init_verbs[] = { {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Setup default input of PW4 to MW0 */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* PW6 PW7 Output enable */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* mixer enable */ {0x1, 0xF88, 0x3}, /* GPIO 0~2 */ @@ -2998,7 +3008,6 @@ static int patch_vt1702(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1702_loopbacks; @@ -3029,31 +3038,9 @@ static const struct hda_verb vt1718S_volume_init_verbs[] = { {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - /* Setup default input of Front HP to MW9 */ - {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* PW9 PW10 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - /* PW11 Input enable */ - {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, /* Enable Boost Volume backdoor */ {0x1, 0xf88, 0x8}, - /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Unmute MW4's index 0 */ - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { } }; @@ -3173,7 +3160,6 @@ static int patch_vt1718S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -3267,24 +3253,6 @@ static const struct hda_verb vt1716S_volume_init_verbs[] = { /* MUX Indices: Stereo Mixer = 5 */ {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - - /* Setup default input of SW1 as MW0 */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, - - /* Setup default input of SW4 as AOW0 */ - {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, - - /* PW9 PW10 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - - /* Unmute SW1, PW12 */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* PW12 Output enable */ - {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* Enable Boost Volume backdoor */ {0x1, 0xf8a, 0x80}, /* don't bybass mixer */ @@ -3442,7 +3410,6 @@ static int patch_vt1716S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -3481,31 +3448,9 @@ static const struct hda_verb vt2002P_volume_init_verbs[] = { {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/8 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x37, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, - - /* set PW0 index=0 (MW0) */ - {0x24, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0x88}, { } @@ -3742,7 +3687,6 @@ static int patch_vt2002P(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -3777,31 +3721,9 @@ static const struct hda_verb vt1812_volume_init_verbs[] = { {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/13/15 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x38, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0xa8}, { } @@ -3948,7 +3870,6 @@ static int patch_vt1812(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE -- cgit v1.2.3 From 096a885494f6b89a9962c6faf18e1c6092e7919c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 12:09:02 +0200 Subject: ALSA: hda - Initialize input-path dynamically in patch_via.c Similarly like the previous commit, initialize the input-paths dynamically from the parsed results instead of the fixed array for VIA codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 363 +++++++--------------------------------------- 1 file changed, 49 insertions(+), 314 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4f6e7bebdb45..68f435dbbfd4 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -629,9 +629,18 @@ static void via_auto_init_analog_input(struct hda_codec *codec) { struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; unsigned int ctl; - int i; + int i, num_conns; + + /* init ADCs */ + for (i = 0; i < spec->num_adc_nids; i++) { + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + /* init pins */ for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; if (spec->smart51_enabled && is_smart51_pins(codec, nid)) @@ -643,6 +652,29 @@ static void via_auto_init_analog_input(struct hda_codec *codec) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); } + + /* init input-src */ + for (i = 0; i < spec->num_adc_nids; i++) { + const struct hda_input_mux *imux = spec->input_mux; + if (!imux || !spec->mux_nids[i]) + continue; + snd_hda_codec_write(codec, spec->mux_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[spec->cur_mux[i]].index); + } + + /* init aa-mixer */ + if (!spec->aa_mix_nid) + return; + num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn)); + for (i = 0; i < num_conns; i++) { + unsigned int caps = get_wcaps(codec, conn[i]); + if (get_wcaps_type(caps) == AC_WID_PIN) + snd_hda_codec_write(codec, spec->aa_mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } } static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, @@ -1117,24 +1149,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) /* * generic initialization of ADC, input mixers and output mixers */ -static const struct hda_verb vt1708_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - +static const struct hda_verb vt1708_init_verbs[] = { /* power down jack detect function */ {0x1, 0xf81, 0x1}, { } @@ -2200,7 +2215,7 @@ static int via_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) spec->mixers[spec->num_mixers++] = spec->kctls.list; - spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; spec->input_mux = &spec->private_imux[0]; @@ -2354,30 +2369,6 @@ static const struct hda_verb vt1709_uniwill_init_verbs[] = { /* * generic initialization of ADC, input mixers and output mixers */ -static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* Set input of PW4 as MW0 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1709_loopbacks[] = { { 0x18, HDA_INPUT, 1 }, @@ -2406,7 +2397,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; codec->patch_ops = via_patch_ops; @@ -2421,46 +2411,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) /* * generic initialization of ADC, input mixers and output mixers */ -static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output selector (0x1a, 0x1b, 0x29) - */ - /* set vol=0 to output mixers */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Unmute PW3 and PW4 - */ - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Set input of PW4 as MW0 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - { } -}; - static int patch_vt1709_6ch(struct hda_codec *codec) { struct via_spec *spec; @@ -2479,7 +2429,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; codec->patch_ops = via_patch_ops; @@ -2494,62 +2443,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) /* * generic initialization of ADC, input mixers and output mixers */ -static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers - */ - /* set vol=0 to output mixers */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* PW9 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* PW10 Input enable */ - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - { } -}; - -static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* PW10 Input enable */ - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - { } -}; - static const struct hda_verb vt1708B_uniwill_init_verbs[] = { {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, @@ -2675,7 +2568,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; codec->patch_ops = via_patch_ops; @@ -2707,7 +2599,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; codec->patch_ops = via_patch_ops; @@ -2723,21 +2614,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) } /* Patch for VT1708S */ - -static const struct hda_verb vt1708S_volume_init_verbs[] = { - /* Unmute ADC0-1 and set the default input to mic-in */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the - * analog-loopback mixer widget */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - +static const struct hda_verb vt1708S_init_verbs[] = { /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, /* don't bybass mixer */ @@ -2863,7 +2740,7 @@ static int patch_vt1708S(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; if (codec->vendor_id == 0x11064397) spec->init_verbs[spec->num_iverbs++] = vt1705_uniwill_init_verbs; @@ -2900,25 +2777,7 @@ static int patch_vt1708S(struct hda_codec *codec) /* Patch for VT1702 */ -static const struct hda_verb vt1702_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - +static const struct hda_verb vt1702_init_verbs[] = { /* mixer enable */ {0x1, 0xF88, 0x3}, /* GPIO 0~2 */ @@ -3003,7 +2862,7 @@ static int patch_vt1702(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; codec->patch_ops = via_patch_ops; @@ -3019,25 +2878,9 @@ static int patch_vt1702(struct hda_codec *codec) /* Patch for VT1718S */ -static const struct hda_verb vt1718S_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - +static const struct hda_verb vt1718S_init_verbs[] = { /* Enable MW0 adjust Gain 5 */ {0x1, 0xfb2, 0x10}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - /* Enable Boost Volume backdoor */ {0x1, 0xf88, 0x8}, @@ -3155,7 +2998,7 @@ static int patch_vt1718S(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; codec->patch_ops = via_patch_ops; @@ -3232,27 +3075,7 @@ static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { { } /* end */ }; -static const struct hda_verb vt1716S_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Stereo Mixer = 5 */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, - +static const struct hda_verb vt1716S_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xf8a, 0x80}, /* don't bybass mixer */ @@ -3400,7 +3223,7 @@ static int patch_vt1716S(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; @@ -3422,86 +3245,20 @@ static int patch_vt1716S(struct hda_codec *codec) /* for vt2002P */ -static const struct hda_verb vt2002P_volume_init_verbs[] = { +static const struct hda_verb vt2002P_init_verbs[] = { /* Class-D speaker related verbs */ {0x1, 0xfe0, 0x4}, {0x1, 0xfe9, 0x80}, {0x1, 0xfe2, 0x22}, - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0x88}, { } }; -static const struct hda_verb vt1802_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - +static const struct hda_verb vt1802_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - - /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/8 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x38, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, - - /* set PW0 index=0 (MW0) */ - {0x24, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0x88}, { } @@ -3673,10 +3430,10 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec->codec_type == VT1802) spec->init_verbs[spec->num_iverbs++] = - vt1802_volume_init_verbs; + vt1802_init_verbs; else spec->init_verbs[spec->num_iverbs++] = - vt2002P_volume_init_verbs; + vt2002P_init_verbs; if (spec->codec_type == VT1802) spec->init_verbs[spec->num_iverbs++] = @@ -3699,31 +3456,9 @@ static int patch_vt2002P(struct hda_codec *codec) /* for vt1812 */ -static const struct hda_verb vt1812_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - +static const struct hda_verb vt1812_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0xa8}, { } @@ -3865,7 +3600,7 @@ static int patch_vt1812(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; codec->patch_ops = via_patch_ops; -- cgit v1.2.3 From 4a918ffeaadd6a2269b9c6575478c102382c7702 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 12:39:26 +0200 Subject: ALSA: hda - Initialize unsol events dynamically in patch_via.c Issue the init verbs of unsolicited events dynamically from the parsed results for VIA codecs. Also, consolidate the unsol handlers for HP and line-out mutes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 444 ++++++++++------------------------------------ 1 file changed, 98 insertions(+), 346 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 68f435dbbfd4..1edcd3221c98 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -138,6 +138,7 @@ struct via_spec { struct nid_path out_path[4]; struct nid_path hp_path; struct nid_path hp_dep_path; + struct nid_path speaker_path; /* capture */ unsigned int num_adc_nids; @@ -252,15 +253,12 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_JACK_EVENT 0x20 #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 -#define VIA_MONO_EVENT 0x03 -#define VIA_SPEAKER_EVENT 0x04 -#define VIA_BIND_HP_EVENT 0x05 +#define VIA_LINE_EVENT 0x03 enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, - VIA_CTL_WIDGET_BIND_PIN_MUTE, }; static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); @@ -323,106 +321,10 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, .put = analog_input_switch_put, \ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } -static void via_hp_bind_automute(struct hda_codec *codec); - -static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int i; - int change = 0; - - long *valp = ucontrol->value.integer.value; - int lmute, rmute; - if (strstr(kcontrol->id.name, "Switch") == NULL) { - snd_printd("Invalid control!\n"); - return change; - } - change = snd_hda_mixer_amp_switch_put(kcontrol, - ucontrol); - /* Get mute value */ - lmute = *valp ? 0 : HDA_AMP_MUTE; - valp++; - rmute = *valp ? 0 : HDA_AMP_MUTE; - - /* Set hp pins */ - if (!spec->hp_independent_mode) { - for (i = 0; i < spec->autocfg.hp_outs; i++) { - snd_hda_codec_amp_update( - codec, spec->autocfg.hp_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - snd_hda_codec_amp_update( - codec, spec->autocfg.hp_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - } - } - - if (!lmute && !rmute) { - /* Line Outs */ - for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[i], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - /* Speakers */ - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[i], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - /* unmute */ - via_hp_bind_automute(codec); - - } else { - if (lmute) { - /* Mute all left channels */ - for (i = 1; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.line_out_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.speaker_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - } - if (rmute) { - /* mute all right channels */ - for (i = 1; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.line_out_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.speaker_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - } - } - return change; -} - -#define BIND_PIN_MUTE \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = NULL, \ - .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = bind_pin_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - static const struct snd_kcontrol_new via_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, - BIND_PIN_MUTE, }; @@ -623,6 +525,15 @@ static void via_auto_init_hp_out(struct hda_codec *codec) &spec->hp_dep_path); } +static void via_auto_init_speaker_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (spec->autocfg.speaker_outs) + via_auto_init_output(codec, spec->autocfg.speaker_pins[0], + PIN_OUT, &spec->speaker_path); +} + static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); static void via_auto_init_analog_input(struct hda_codec *codec) @@ -1554,46 +1465,34 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins, mute ? 0 : PIN_OUT); } -/* mute internal speaker if HP is plugged */ -static void via_hp_automute(struct hda_codec *codec) +/* mute internal speaker if line-out is plugged */ +static void via_line_automute(struct hda_codec *codec, int present) { - unsigned int present = 0; struct via_spec *spec = codec->spec; - present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) - toggle_output_mutes(codec, spec->autocfg.line_outs, - spec->autocfg.line_out_pins, - present); + if (!spec->autocfg.speaker_outs) + return; + if (!present) + present = snd_hda_jack_detect(codec, + spec->autocfg.line_out_pins[0]); + toggle_output_mutes(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + present); } -/* mute mono out if HP or Line out is plugged */ -static void via_mono_automute(struct hda_codec *codec) +/* mute internal speaker if HP is plugged */ +static void via_hp_automute(struct hda_codec *codec) { - unsigned int hp_present, lineout_present; + int present = 0; struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1716S) - return; - - lineout_present = snd_hda_jack_detect(codec, - spec->autocfg.line_out_pins[0]); - - /* Mute Mono Out if Line Out is plugged */ - if (lineout_present) { - snd_hda_codec_write(codec, 0x2A, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - lineout_present ? 0 : PIN_OUT); - return; + if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); + toggle_output_mutes(codec, spec->autocfg.line_outs, + spec->autocfg.line_out_pins, + present); } - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) - snd_hda_codec_write(codec, 0x2A, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - hp_present ? 0 : PIN_OUT); + via_line_automute(codec, present); } static void via_gpio_control(struct hda_codec *codec) @@ -1643,49 +1542,6 @@ static void via_gpio_control(struct hda_codec *codec) } } -/* mute Internal-Speaker if HP is plugged */ -static void via_speaker_automute(struct hda_codec *codec) -{ - unsigned int hp_present; - struct via_spec *spec = codec->spec; - - if (!VT2002P_COMPATIBLE(spec)) - return; - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) - toggle_output_mutes(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - hp_present); -} - -/* mute line-out and internal speaker if HP is plugged */ -static void via_hp_bind_automute(struct hda_codec *codec) -{ - int present; - struct via_spec *spec = codec->spec; - - if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) - return; - - present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - if (!spec->hp_independent_mode) - toggle_output_mutes(codec, spec->autocfg.line_outs, - spec->autocfg.line_out_pins, - present); - - if (!present) - present = snd_hda_jack_detect(codec, - spec->autocfg.line_out_pins[0]); - - /* Speakers */ - toggle_output_mutes(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - present); -} - - /* unsolicited event for jack sensing */ static void via_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1701,12 +1557,8 @@ static void via_unsol_event(struct hda_codec *codec, via_hp_automute(codec); else if (res == VIA_GPIO_EVENT) via_gpio_control(codec); - else if (res == VIA_MONO_EVENT) - via_mono_automute(codec); - else if (res == VIA_SPEAKER_EVENT) - via_speaker_automute(codec); - else if (res == VIA_BIND_HP_EVENT) - via_hp_bind_automute(codec); + else if (res == VIA_LINE_EVENT) + via_line_automute(codec, false); } #ifdef SND_HDA_NEEDS_RESUME @@ -1736,6 +1588,7 @@ static const struct hda_codec_ops via_patch_ops = { .build_pcms = via_build_pcms, .init = via_init, .free = via_free, + .unsol_event = via_unsol_event, #ifdef SND_HDA_NEEDS_RESUME .suspend = via_suspend, #endif @@ -1968,6 +1821,27 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) return 0; } +static int via_auto_create_speaker_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t pin, dac; + + pin = spec->autocfg.speaker_pins[0]; + if (!spec->autocfg.speaker_outs || !pin) + return 0; + + if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) { + dac = spec->speaker_path.path[spec->speaker_path.depth - 1]; + spec->multiout.extra_out_nid[0] = dac; + return create_ch_ctls(codec, "Speaker", pin, dac, 3); + } + if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + &spec->speaker_path, 0, -1)) + return create_ch_ctls(codec, "Headphone", pin, 0, 3); + + return 0; +} + /* look for ADCs */ static int via_fill_adcs(struct hda_codec *codec) { @@ -2201,6 +2075,9 @@ static int via_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = via_auto_create_speaker_ctls(codec); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); @@ -2254,6 +2131,39 @@ static void via_auto_init_dig_in(struct hda_codec *codec) AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); } +/* initialize the unsolicited events */ +static void via_auto_init_unsol_event(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int ev; + int i; + + if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) + snd_hda_codec_write(codec, cfg->hp_pins[0], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); + + if (cfg->speaker_pins[0]) + ev = VIA_LINE_EVENT; + else + ev = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (cfg->line_out_pins[i] && + is_jack_detectable(codec, cfg->line_out_pins[i])) + snd_hda_codec_write(codec, cfg->line_out_pins[0], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ev | VIA_JACK_EVENT); + } + + for (i = 0; i < cfg->num_inputs; i++) { + if (is_jack_detectable(codec, cfg->inputs[i].pin)) + snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT); + } +} + static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2264,16 +2174,15 @@ static int via_init(struct hda_codec *codec) via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); + via_auto_init_speaker_out(codec); via_auto_init_analog_input(codec); via_auto_init_dig_outs(codec); via_auto_init_dig_in(codec); - if (VT2002P_COMPATIBLE(spec)) { - via_hp_bind_automute(codec); - } else { - via_hp_automute(codec); - via_speaker_automute(codec); - } + via_auto_init_unsol_event(codec); + + via_hp_automute(codec); + via_line_automute(codec, false); return 0; } @@ -2360,12 +2269,6 @@ static int patch_vt1708(struct hda_codec *codec) return 0; } -static const struct hda_verb vt1709_uniwill_init_verbs[] = { - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - { } -}; - /* * generic initialization of ADC, input mixers and output mixers */ @@ -2397,11 +2300,8 @@ static int patch_vt1709_10ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1709_loopbacks; #endif @@ -2429,11 +2329,8 @@ static int patch_vt1709_6ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1709_loopbacks; #endif @@ -2443,19 +2340,6 @@ static int patch_vt1709_6ch(struct hda_codec *codec) /* * generic initialization of ADC, input mixers and output mixers */ -static const struct hda_verb vt1708B_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1708B_loopbacks[] = { { 0x16, HDA_INPUT, 1 }, @@ -2568,11 +2452,8 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708B_loopbacks; #endif @@ -2599,11 +2480,8 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) return err; } - spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708B_loopbacks; #endif @@ -2622,31 +2500,6 @@ static const struct hda_verb vt1708S_init_verbs[] = { { } }; -static const struct hda_verb vt1708S_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_verb vt1705_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - /* fill out digital output widgets; one for master and one for slave outputs */ static void fill_dig_outs(struct hda_codec *codec) { @@ -2741,16 +2594,9 @@ static int patch_vt1708S(struct hda_codec *codec) } spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; - if (codec->vendor_id == 0x11064397) - spec->init_verbs[spec->num_iverbs++] = - vt1705_uniwill_init_verbs; - else - spec->init_verbs[spec->num_iverbs++] = - vt1708S_uniwill_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708S_loopbacks; #endif @@ -2785,16 +2631,6 @@ static const struct hda_verb vt1702_init_verbs[] = { { } }; -static const struct hda_verb vt1702_uniwill_init_verbs[] = { - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1702_loopbacks[] = { { 0x1A, HDA_INPUT, 1 }, @@ -2863,11 +2699,9 @@ static int patch_vt1702(struct hda_codec *codec) } spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1702_loopbacks; #endif @@ -2887,20 +2721,6 @@ static const struct hda_verb vt1718S_init_verbs[] = { { } }; - -static const struct hda_verb vt1718S_uniwill_init_verbs[] = { - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1718S_loopbacks[] = { { 0x21, HDA_INPUT, 1 }, @@ -2999,12 +2819,9 @@ static int patch_vt1718S(struct hda_codec *codec) } spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; - #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1718S_loopbacks; #endif @@ -3085,20 +2902,6 @@ static const struct hda_verb vt1716S_init_verbs[] = { { } }; - -static const struct hda_verb vt1716S_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1716S_loopbacks[] = { { 0x16, HDA_INPUT, 1 }, @@ -3224,7 +3027,6 @@ static int patch_vt1716S(struct hda_codec *codec) } spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; spec->num_mixers++; @@ -3233,8 +3035,6 @@ static int patch_vt1716S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; - #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1716S_loopbacks; #endif @@ -3256,6 +3056,7 @@ static const struct hda_verb vt2002P_init_verbs[] = { {0x1, 0xfb8, 0x88}, { } }; + static const struct hda_verb vt1802_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, @@ -3264,28 +3065,6 @@ static const struct hda_verb vt1802_init_verbs[] = { { } }; - -static const struct hda_verb vt2002P_uniwill_init_verbs[] = { - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; -static const struct hda_verb vt1802_uniwill_init_verbs[] = { - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt2002P_loopbacks[] = { { 0x21, HDA_INPUT, 0 }, @@ -3429,23 +3208,12 @@ static int patch_vt2002P(struct hda_codec *codec) } if (spec->codec_type == VT1802) - spec->init_verbs[spec->num_iverbs++] = - vt1802_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; else - spec->init_verbs[spec->num_iverbs++] = - vt2002P_init_verbs; - - if (spec->codec_type == VT1802) - spec->init_verbs[spec->num_iverbs++] = - vt1802_uniwill_init_verbs; - else - spec->init_verbs[spec->num_iverbs++] = - vt2002P_uniwill_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; - #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt2002P_loopbacks; #endif @@ -3464,19 +3232,6 @@ static const struct hda_verb vt1812_init_verbs[] = { { } }; - -static const struct hda_verb vt1812_uniwill_init_verbs[] = { - {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list vt1812_loopbacks[] = { { 0x21, HDA_INPUT, 0 }, @@ -3601,12 +3356,9 @@ static int patch_vt1812(struct hda_codec *codec) } spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.unsol_event = via_unsol_event; - #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1812_loopbacks; #endif -- cgit v1.2.3 From 370bafbdae3d78c9081ebe3028a3ff5f0e91357b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 12:47:45 +0200 Subject: ALSA: hda - Create virtual-master control for VIA codecs Now let's add the missing Master control to VIA codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 1edcd3221c98..deb33ae109c8 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1308,6 +1308,31 @@ static const struct hda_pcm_stream via_pcm_digital_capture = { .channels_max = 2, }; +/* + * slave controls for virtual master + */ +static const char * const via_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + NULL, +}; + +static const char * const via_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Speaker Playback Switch", + NULL, +}; + static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1343,6 +1368,23 @@ static int via_build_controls(struct hda_codec *codec) return err; } + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + HDA_OUTPUT, vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, via_slave_vols); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, via_slave_sws); + if (err < 0) + return err; + } + /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); for (i = 0; kctl && i < kctl->count; i++) { -- cgit v1.2.3 From e3d7a1431f1d8851d11b2262dda5bb67158450eb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 13:52:33 +0200 Subject: ALSA: hda - Fix smart51 handling again Fix the broken detection of smart51 and its handling. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 147 ++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 83 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index deb33ae109c8..c3be9f124b68 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -165,12 +165,17 @@ struct via_spec { const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; unsigned int hp_independent_mode_index; - unsigned int can_smart51; - unsigned int smart51_enabled; unsigned int dmic_enabled; unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; + /* smart51 setup */ + unsigned int smart51_nums; + hda_nid_t smart51_pins[2]; + int smart51_idxs[2]; + const char *smart51_labels[2]; + unsigned int smart51_enabled; + /* work to check hp jack state */ struct hda_codec *codec; struct delayed_work vt1708_hp_work; @@ -508,7 +513,7 @@ static void via_auto_init_multi_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; int i; - for (i = 0; i < spec->autocfg.line_outs; i++) + for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) via_auto_init_output(codec, spec->autocfg.line_out_pins[i], PIN_OUT, &spec->out_path[i]); } @@ -771,15 +776,15 @@ static int via_hp_build(struct hda_codec *codec) static void notify_aa_path_ctls(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; int i; - struct snd_ctl_elem_id id; - const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; - struct snd_kcontrol *ctl; - - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - for (i = 0; i < ARRAY_SIZE(labels); i++) { - sprintf(id.name, "%s Playback Volume", labels[i]); + + for (i = 0; i < spec->smart51_nums; i++) { + struct snd_kcontrol *ctl; + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); ctl = snd_hda_find_mixer_ctl(codec, id.name); if (ctl) snd_ctl_notify(codec->bus->card, @@ -791,43 +796,20 @@ static void notify_aa_path_ctls(struct hda_codec *codec) static void mute_aa_path(struct hda_codec *codec, int mute) { struct via_spec *spec = codec->spec; - int start_idx; - int end_idx; + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; int i; - /* get nid of MW0 and start & end index */ - switch (spec->codec_type) { - case VT1708: - start_idx = 2; - end_idx = 4; - break; - case VT1709_10CH: - case VT1709_6CH: - start_idx = 2; - end_idx = 4; - break; - case VT1708B_8CH: - case VT1708B_4CH: - case VT1708S: - case VT1716S: - start_idx = 2; - end_idx = 4; - break; - case VT1718S: - start_idx = 1; - end_idx = 3; - break; - default: - return; - } + /* check AA path's mute status */ - for (i = start_idx; i <= end_idx; i++) { - int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; - snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, + for (i = 0; i < spec->smart51_nums; i++) { + if (spec->smart51_idxs[i] < 0) + continue; + snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, + HDA_INPUT, spec->smart51_idxs[i], HDA_AMP_MUTE, val); } } -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) +static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; @@ -847,6 +829,17 @@ static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) return false; } +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->smart51_nums; i++) + if (spec->smart51_pins[i] == pin) + return true; + return false; +} + static int via_smart51_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -862,18 +855,12 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; int on = 1; int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; + for (i = 0; i < spec->smart51_nums; i++) { + hda_nid_t nid = spec->smart51_pins[i]; unsigned int ctl; - if (cfg->inputs[i].type == AUTO_PIN_MIC && - spec->hp_independent_mode && spec->codec_type != VT1718S) - continue; /* ignore FMic for independent HP */ - if (!is_smart51_pins(codec, nid)) - continue; ctl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) @@ -888,21 +875,14 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; int out_in = *ucontrol->value.integer.value ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; + for (i = 0; i < spec->smart51_nums; i++) { + hda_nid_t nid = spec->smart51_pins[i]; unsigned int parm; - if (cfg->inputs[i].type == AUTO_PIN_MIC && - spec->hp_independent_mode && spec->codec_type != VT1718S) - continue; /* don't retask FMic for independent HP */ - if (!is_smart51_pins(codec, nid)) - continue; - parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); @@ -932,26 +912,11 @@ static const struct snd_kcontrol_new via_smart51_mixer = { static int via_smart51_build(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; - if (!spec->can_smart51) + if (!spec->smart51_nums) return 0; - - knew = via_clone_control(spec, &via_smart51_mixer); - if (knew == NULL) + if (!via_clone_control(spec, &via_smart51_mixer)) return -ENOMEM; - - for (i = 0; i < cfg->num_inputs; i++) { - nid = cfg->inputs[i].pin; - if (is_smart51_pins(codec, nid)) { - knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - break; - } - } - return 0; } @@ -1751,12 +1716,18 @@ static void mangle_smart51(struct hda_codec *codec) { struct via_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int i; + int i, nums = 0; for (i = 0; i < cfg->num_inputs; i++) { - if (!is_smart51_pins(codec, cfg->inputs[i].pin)) + if (is_smart51_candidate(codec, cfg->inputs[i].pin)) + nums++; + } + if (cfg->line_outs + nums < 3) + return; + for (i = 0; i < cfg->num_inputs; i++) { + if (!is_smart51_candidate(codec, cfg->inputs[i].pin)) continue; - spec->can_smart51 = 1; + spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin; cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; if (cfg->line_outs == 3) break; @@ -1779,6 +1750,10 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (cfg->line_outs == 1) mangle_smart51(codec); + err = via_auto_fill_dac_nids(codec); + if (err < 0) + return err; + for (i = 0; i < cfg->line_outs; i++) { hda_nid_t pin, dac; pin = cfg->line_out_pins[i]; @@ -1926,7 +1901,7 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, { struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, idx2, type, type_idx = 0; + int i, j, err, idx, idx2, type, type_idx = 0; hda_nid_t cap_nid; hda_nid_t pin_idxs[8]; int num_idxs; @@ -1973,6 +1948,15 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, if (err < 0) return err; snd_hda_add_imux_item(imux, label, idx, NULL); + + /* remember the label for smart51 control */ + for (j = 0; j < spec->smart51_nums; j++) { + if (spec->smart51_pins[j] == cfg->inputs[i].pin) { + spec->smart51_idxs[j] = idx; + spec->smart51_labels[j] = label; + break; + } + } } /* create capture mixer elements */ @@ -2105,9 +2089,6 @@ static int via_parse_auto_config(struct hda_codec *codec) int err; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = via_auto_fill_dac_nids(codec); if (err < 0) return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) -- cgit v1.2.3 From 13af8e77ea3e0dff80db9b2e0007535c21d49094 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 14:05:46 +0200 Subject: ALSA: hda - Create loopback-list dynamically in patch_via.c Create loopback list dynamically from the parsed input pins for VIA codecs instead of the fixed arrays. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 164 +++++++--------------------------------------- 1 file changed, 25 insertions(+), 139 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c3be9f124b68..bd6ffa602f22 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -186,6 +186,8 @@ struct via_spec { #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; #endif }; @@ -1895,6 +1897,24 @@ static const struct snd_kcontrol_new via_input_src_ctl = { .put = via_mux_enum_put, }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} +#else +#define add_loopback_list(spec, mix, idx) /* NOP */ +#endif + /* create playback/capture controls for input pins */ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) @@ -1942,11 +1962,13 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, label = hda_get_autocfg_input_label(codec, cfg, i); idx2 = get_connection_index(codec, spec->aa_mix_nid, pin_idxs[idx]); - if (idx2 >= 0) + if (idx2 >= 0) { err = via_new_analog_input(spec, label, type_idx, idx2, spec->aa_mix_nid); - if (err < 0) - return err; + if (err < 0) + return err; + add_loopback_list(spec, spec->aa_mix_nid, idx2); + } snd_hda_add_imux_item(imux, label, idx, NULL); /* remember the label for smart51 control */ @@ -2011,16 +2033,6 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1708_loopbacks[] = { - { 0x17, HDA_INPUT, 1 }, - { 0x17, HDA_INPUT, 2 }, - { 0x17, HDA_INPUT, 3 }, - { 0x17, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) { unsigned int def_conf; @@ -2285,26 +2297,10 @@ static int patch_vt1708(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708_loopbacks; -#endif INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } -/* - * generic initialization of ADC, input mixers and output mixers - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1709_loopbacks[] = { - { 0x18, HDA_INPUT, 1 }, - { 0x18, HDA_INPUT, 2 }, - { 0x18, HDA_INPUT, 3 }, - { 0x18, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static int patch_vt1709_10ch(struct hda_codec *codec) { struct via_spec *spec; @@ -2325,10 +2321,6 @@ static int patch_vt1709_10ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1709_loopbacks; -#endif - return 0; } /* @@ -2354,25 +2346,9 @@ static int patch_vt1709_6ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1709_loopbacks; -#endif return 0; } -/* - * generic initialization of ADC, input mixers and output mixers - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1708B_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1708B(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2477,10 +2453,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708B_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; @@ -2505,10 +2477,6 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708B_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; @@ -2575,16 +2543,6 @@ static void fill_dig_in(struct hda_codec *codec) } } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1708S_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, int offset, int num_steps, int step_size) { @@ -2620,10 +2578,6 @@ static int patch_vt1708S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708S_loopbacks; -#endif - /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { kfree(codec->chip_name); @@ -2654,16 +2608,6 @@ static const struct hda_verb vt1702_init_verbs[] = { { } }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1702_loopbacks[] = { - { 0x1A, HDA_INPUT, 1 }, - { 0x1A, HDA_INPUT, 2 }, - { 0x1A, HDA_INPUT, 3 }, - { 0x1A, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1702(struct hda_codec *codec) { int imux_is_smixer = @@ -2725,10 +2669,6 @@ static int patch_vt1702(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1702_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1702; return 0; } @@ -2744,16 +2684,6 @@ static const struct hda_verb vt1718S_init_verbs[] = { { } }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1718S_loopbacks[] = { - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { 0x21, HDA_INPUT, 3 }, - { 0x21, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1718S(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2845,10 +2775,6 @@ static int patch_vt1718S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1718S_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1718S; return 0; @@ -2925,16 +2851,6 @@ static const struct hda_verb vt1716S_init_verbs[] = { { } }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1716S_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1716S(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3058,10 +2974,6 @@ static int patch_vt1716S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1716S_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1716S; return 0; } @@ -3088,15 +3000,6 @@ static const struct hda_verb vt1802_init_verbs[] = { { } }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt2002P_loopbacks[] = { - { 0x21, HDA_INPUT, 0 }, - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt2002P(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3237,10 +3140,6 @@ static int patch_vt2002P(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt2002P_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt2002P; return 0; } @@ -3255,15 +3154,6 @@ static const struct hda_verb vt1812_init_verbs[] = { { } }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1812_loopbacks[] = { - { 0x21, HDA_INPUT, 0 }, - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1812(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3382,10 +3272,6 @@ static int patch_vt1812(struct hda_codec *codec) codec->patch_ops = via_patch_ops; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1812_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1812; return 0; } -- cgit v1.2.3 From 6aadf41d6b9f8da68db5962929c07f816db15893 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 14:09:02 +0200 Subject: ALSA: hda - Name the primary out as Speaker when needed for VIA codecs When the primary output is the speaker output, rather name it as "Speaker". This will be more intuitive. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index bd6ffa602f22..6e621b7c984e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1770,7 +1770,11 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (err < 0) return err; } else { - err = create_ch_ctls(codec, chname[i], pin, dac, 3); + const char *pfx = chname[i]; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs == 1) + pfx = "Speaker"; + err = create_ch_ctls(codec, pfx, pin, dac, 3); if (err < 0) return err; } -- cgit v1.2.3 From c6191607871776e828b8bc47b944d0c425776951 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 14:11:59 +0200 Subject: ALSA: hda - Remove unused defines and struct fields in patch_via.c Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 6e621b7c984e..adb04c1c7053 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -56,34 +56,10 @@ #define NID_MAPPING (-1) -/* amp values */ -#define AMP_VAL_IDX_SHIFT 19 -#define AMP_VAL_IDX_MASK (0x0f<<19) - /* Pin Widget NID */ -#define VT1708_HP_NID 0x13 -#define VT1708_DIGOUT_NID 0x14 -#define VT1708_DIGIN_NID 0x16 -#define VT1708_DIGIN_PIN 0x26 #define VT1708_HP_PIN_NID 0x20 #define VT1708_CD_PIN_NID 0x24 -#define VT1709_HP_DAC_NID 0x28 -#define VT1709_DIGOUT_NID 0x13 -#define VT1709_DIGIN_NID 0x17 -#define VT1709_DIGIN_PIN 0x25 - -#define VT1708B_HP_NID 0x25 -#define VT1708B_DIGOUT_NID 0x12 -#define VT1708B_DIGIN_NID 0x15 -#define VT1708B_DIGIN_PIN 0x21 - -#define VT1708S_HP_NID 0x25 -#define VT1708S_DIGOUT_NID 0x12 - -#define VT1702_HP_NID 0x17 -#define VT1702_DIGOUT_NID 0x11 - enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, @@ -146,7 +122,6 @@ struct via_spec { hda_nid_t mux_nids[3]; hda_nid_t aa_mix_nid; hda_nid_t dig_in_nid; - hda_nid_t dig_in_pin; /* capture source */ const struct hda_input_mux *input_mux; -- cgit v1.2.3 From 47be05ce0a634779e1e86ec318a046f43dd6c602 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 15:10:28 +0200 Subject: ALSA: hda - Remove NID_MAPPING hacks in patch_via.c There is no longer virtual kmixer element for NID mapping. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index adb04c1c7053..6b4a6b7a6c7a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,8 +54,6 @@ #include "hda_codec.h" #include "hda_local.h" -#define NID_MAPPING (-1) - /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 #define VT1708_CD_PIN_NID 0x24 @@ -1279,7 +1277,6 @@ static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; struct snd_kcontrol *kctl; - const struct snd_kcontrol_new *knew; int err, i; if (spec->set_widgets_power_state) @@ -1335,19 +1332,6 @@ static int via_build_controls(struct hda_codec *codec) return err; } - /* other nid->control mapping */ - for (i = 0; i < spec->num_mixers; i++) { - for (knew = spec->mixers[i]; knew->name; knew++) { - if (knew->iface != NID_MAPPING) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - if (kctl == NULL) - continue; - err = snd_hda_add_nid(codec, kctl, 0, - knew->subdevice); - } - } - /* init power states */ set_widgets_power_state(codec); analog_low_current_mode(codec, 1); -- cgit v1.2.3 From ada509ec00e4ae1bfc4e0fa8a5c14091df920dbc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Jun 2011 15:40:19 +0200 Subject: ALSA: hda - Simplify analog-low-current mode check for VIA codecs Use the existing aa-loop list for simplifying the check for analog low-current mode. Also fix the stream count test for playback streams. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 125 ++++++++++++++++------------------------------ 1 file changed, 43 insertions(+), 82 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 6b4a6b7a6c7a..819267a4e2df 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -108,6 +108,7 @@ struct via_spec { struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; hda_nid_t hp_dac_nid; + int num_active_streams; struct nid_path out_path[4]; struct nid_path hp_path; @@ -157,11 +158,9 @@ struct via_spec { void (*set_widgets_power_state)(struct hda_codec *codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; int num_loopbacks; struct hda_amp_list loopback_list[8]; -#endif }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -241,8 +240,8 @@ enum { VIA_CTL_WIDGET_ANALOG_MUTE, }; -static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); -static int is_aa_path_mute(struct hda_codec *codec); +static void analog_low_current_mode(struct hda_codec *codec); +static bool is_aa_path_mute(struct hda_codec *codec); static void vt1708_start_hp_work(struct via_spec *spec) { @@ -281,7 +280,7 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); set_widgets_power_state(codec); - analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + analog_low_current_mode(snd_kcontrol_chip(kcontrol)); if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { if (is_aa_path_mute(codec)) vt1708_start_hp_work(codec->spec); @@ -895,77 +894,33 @@ static int via_smart51_build(struct hda_codec *codec) return 0; } -/* check AA path's mute statue */ -static int is_aa_path_mute(struct hda_codec *codec) +/* check AA path's mute status */ +static bool is_aa_path_mute(struct hda_codec *codec) { - int mute = 1; - int start_idx; - int end_idx; - int i; struct via_spec *spec = codec->spec; - /* get nid of MW0 and start & end index */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - case VT1708S: - case VT1716S: - start_idx = 2; - end_idx = 4; - break; - case VT1702: - start_idx = 1; - end_idx = 3; - break; - case VT1718S: - start_idx = 1; - end_idx = 3; - break; - case VT2002P: - case VT1812: - case VT1802: - start_idx = 0; - end_idx = 2; - break; - default: - return 0; - } - /* check AA path's mute status */ - for (i = start_idx; i <= end_idx; i++) { - unsigned int con_list = snd_hda_codec_read( - codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); - int shift = 8 * (i % 4); - hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; - unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); - if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { - /* check mute status while the pin is connected */ - int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, - HDA_INPUT, i) >> 7; - int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, - HDA_INPUT, i) >> 7; - if (!mute_l || !mute_r) { - mute = 0; - break; - } + const struct hda_amp_list *p; + int i, ch, v; + + for (i = 0; i < spec->num_loopbacks; i++) { + p = &spec->loopback_list[i]; + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) + return false; } } - return mute; + return true; } /* enter/exit analog low-current mode */ -static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) +static void analog_low_current_mode(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - static int saved_stream_idle = 1; /* saved stream idle status */ - int enable = is_aa_path_mute(codec); - unsigned int verb = 0; - unsigned int parm = 0; + bool enable; + unsigned int verb, parm; - if (stream_idle == -1) /* stream status did not change */ - enable = enable && saved_stream_idle; - else { - enable = enable && stream_idle; - saved_stream_idle = stream_idle; - } + enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0); /* decide low current mode's verb & parameter */ switch (spec->codec_type) { @@ -1006,12 +961,15 @@ static const struct hda_verb vt1708_init_verbs[] = { { } }; -static void substream_set_idle(struct hda_codec *codec, - struct snd_pcm_substream *substream) +static void set_stream_active(struct hda_codec *codec, bool active) { - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - analog_low_current_mode(codec, idle); + struct via_spec *spec = codec->spec; + + if (active) + spec->num_active_streams++; + else + spec->num_active_streams--; + analog_low_current_mode(codec); } static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, @@ -1019,12 +977,19 @@ static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + int err; if (!spec->hp_independent_mode) spec->multiout.hp_nid = spec->hp_dac_nid; - substream_set_idle(codec, substream); - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); + set_stream_active(codec, true); + err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); + if (err < 0) { + spec->multiout.hp_nid = 0; + set_stream_active(codec, false); + return err; + } + return 0; } static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, @@ -1034,7 +999,7 @@ static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, struct via_spec *spec = codec->spec; spec->multiout.hp_nid = 0; - substream_set_idle(codec, substream); + set_stream_active(codec, false); return 0; } @@ -1048,7 +1013,7 @@ static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, return -EINVAL; if (!spec->hp_independent_mode || spec->multiout.hp_nid) return -EBUSY; - substream_set_idle(codec, substream); + set_stream_active(codec, true); return 0; } @@ -1056,7 +1021,7 @@ static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - substream_set_idle(codec, substream); + set_stream_active(codec, false); return 0; } @@ -1334,7 +1299,7 @@ static int via_build_controls(struct hda_codec *codec) /* init power states */ set_widgets_power_state(codec); - analog_low_current_mode(codec, 1); + analog_low_current_mode(codec); via_free_kctls(codec); /* no longer needed */ return 0; @@ -1860,7 +1825,6 @@ static const struct snd_kcontrol_new via_input_src_ctl = { .put = via_mux_enum_put, }; -#ifdef CONFIG_SND_HDA_POWER_SAVE static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) { struct hda_amp_list *list; @@ -1874,9 +1838,6 @@ static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) spec->num_loopbacks++; spec->loopback.amplist = spec->loopback_list; } -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif /* create playback/capture controls for input pins */ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, -- cgit v1.2.3 From 95c6e9cb774979c270f0ecb9ec819d02592ec89f Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Wed, 15 Jun 2011 15:35:17 -0700 Subject: ALSA: hda - Add Creative CA0132 HDA codec support Create patch_ca0132.c, to add support for devices featuring the Creative CA0132 HD-audio codec. This driver implements :- * 1 playback subdevice to headphone and speaker * 2 capture subdevices: i - Mic-in ii- Line-in * mixer device Advanced DSP features are not yet included. Developed and maintained by Creative Labs, Inc. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 13 + sound/pci/hda/Makefile | 4 + sound/pci/hda/patch_ca0132.c | 1096 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1113 insertions(+) create mode 100644 sound/pci/hda/patch_ca0132.c diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 0ea5cc60ac78..85217bd96d85 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -171,6 +171,19 @@ config SND_HDA_CODEC_CA0110 snd-hda-codec-ca0110. This module is automatically loaded at probing. +config SND_HDA_CODEC_CA0132 + bool "Build Creative CA0132 codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Creative CA0132 codec support in + snd-hda-intel driver. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-ca0132. + This module is automatically loaded at probing. + config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 17ef3658f34b..87365d5ea2a9 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -13,6 +13,7 @@ snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o +snd-hda-codec-ca0132-objs := patch_ca0132.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o @@ -42,6 +43,9 @@ endif ifdef CONFIG_SND_HDA_CODEC_CA0110 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o endif +ifdef CONFIG_SND_HDA_CODEC_CA0132 +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.o +endif ifdef CONFIG_SND_HDA_CODEC_CONEXANT obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o endif diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c new file mode 100644 index 000000000000..55f58e76cce7 --- /dev/null +++ b/sound/pci/hda/patch_ca0132.c @@ -0,0 +1,1096 @@ +/* + * HD audio interface patch for Creative CA0132 chip + * + * Copyright (c) 2011, Creative Technology Ltd. + * + * Based on patch_ca0110.c + * Copyright (c) 2008 Takashi Iwai + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + +#define WIDGET_CHIP_CTRL 0x15 +#define WIDGET_DSP_CTRL 0x16 + +#define WUH_MEM_CONNID 10 +#define DSP_MEM_CONNID 16 + +enum hda_cmd_vendor_io { + /* for DspIO node */ + VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, + VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100, + + VENDOR_DSPIO_STATUS = 0xF01, + VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702, + VENDOR_DSPIO_SCP_READ_DATA = 0xF02, + VENDOR_DSPIO_DSP_INIT = 0x703, + VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704, + VENDOR_DSPIO_SCP_READ_COUNT = 0xF04, + + /* for ChipIO node */ + VENDOR_CHIPIO_ADDRESS_LOW = 0x000, + VENDOR_CHIPIO_ADDRESS_HIGH = 0x100, + VENDOR_CHIPIO_STREAM_FORMAT = 0x200, + VENDOR_CHIPIO_DATA_LOW = 0x300, + VENDOR_CHIPIO_DATA_HIGH = 0x400, + + VENDOR_CHIPIO_GET_PARAMETER = 0xF00, + VENDOR_CHIPIO_STATUS = 0xF01, + VENDOR_CHIPIO_HIC_POST_READ = 0x702, + VENDOR_CHIPIO_HIC_READ_DATA = 0xF03, + + VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, + + VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C, + VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C, + VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D, + VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E, + VENDOR_CHIPIO_FLAG_SET = 0x70F, + VENDOR_CHIPIO_FLAGS_GET = 0xF0F, + VENDOR_CHIPIO_PARAMETER_SET = 0x710, + VENDOR_CHIPIO_PARAMETER_GET = 0xF10, + + VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711, + VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712, + VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12, + VENDOR_CHIPIO_PORT_FREE_SET = 0x713, + + VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17, + VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717, + VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18, + VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718 +}; + +/* + * Control flag IDs + */ +enum control_flag_id { + /* Connection manager stream setup is bypassed/enabled */ + CONTROL_FLAG_C_MGR = 0, + /* DSP DMA is bypassed/enabled */ + CONTROL_FLAG_DMA = 1, + /* 8051 'idle' mode is disabled/enabled */ + CONTROL_FLAG_IDLE_ENABLE = 2, + /* Tracker for the SPDIF-in path is bypassed/enabled */ + CONTROL_FLAG_TRACKER = 3, + /* DigitalOut to Spdif2Out connection is disabled/enabled */ + CONTROL_FLAG_SPDIF2OUT = 4, + /* Digital Microphone is disabled/enabled */ + CONTROL_FLAG_DMIC = 5, + /* ADC_B rate is 48 kHz/96 kHz */ + CONTROL_FLAG_ADC_B_96KHZ = 6, + /* ADC_C rate is 48 kHz/96 kHz */ + CONTROL_FLAG_ADC_C_96KHZ = 7, + /* DAC rate is 48 kHz/96 kHz (affects all DACs) */ + CONTROL_FLAG_DAC_96KHZ = 8, + /* DSP rate is 48 kHz/96 kHz */ + CONTROL_FLAG_DSP_96KHZ = 9, + /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */ + CONTROL_FLAG_SRC_CLOCK_196MHZ = 10, + /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */ + CONTROL_FLAG_SRC_RATE_96KHZ = 11, + /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */ + CONTROL_FLAG_DECODE_LOOP = 12, + /* De-emphasis filter on DAC-1 disabled/enabled */ + CONTROL_FLAG_DAC1_DEEMPHASIS = 13, + /* De-emphasis filter on DAC-2 disabled/enabled */ + CONTROL_FLAG_DAC2_DEEMPHASIS = 14, + /* De-emphasis filter on DAC-3 disabled/enabled */ + CONTROL_FLAG_DAC3_DEEMPHASIS = 15, + /* High-pass filter on ADC_B disabled/enabled */ + CONTROL_FLAG_ADC_B_HIGH_PASS = 16, + /* High-pass filter on ADC_C disabled/enabled */ + CONTROL_FLAG_ADC_C_HIGH_PASS = 17, + /* Common mode on Port_A disabled/enabled */ + CONTROL_FLAG_PORT_A_COMMON_MODE = 18, + /* Common mode on Port_D disabled/enabled */ + CONTROL_FLAG_PORT_D_COMMON_MODE = 19, + /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */ + CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20, + /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */ + CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21, + /* ASI rate is 48kHz/96kHz */ + CONTROL_FLAG_ASI_96KHZ = 22, + /* DAC power settings able to control attached ports no/yes */ + CONTROL_FLAG_DACS_CONTROL_PORTS = 23, + /* Clock Stop OK reporting is disabled/enabled */ + CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24, + /* Number of control flags */ + CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1) +}; + +/* + * Control parameter IDs + */ +enum control_parameter_id { + /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */ + CONTROL_PARAM_SPDIF1_SOURCE = 2, + + /* Stream Control */ + + /* Select stream with the given ID */ + CONTROL_PARAM_STREAM_ID = 24, + /* Source connection point for the selected stream */ + CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25, + /* Destination connection point for the selected stream */ + CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26, + /* Number of audio channels in the selected stream */ + CONTROL_PARAM_STREAMS_CHANNELS = 27, + /*Enable control for the selected stream */ + CONTROL_PARAM_STREAM_CONTROL = 28, + + /* Connection Point Control */ + + /* Select connection point with the given ID */ + CONTROL_PARAM_CONN_POINT_ID = 29, + /* Connection point sample rate */ + CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30, + + /* Node Control */ + + /* Select HDA node with the given ID */ + CONTROL_PARAM_NODE_ID = 31 +}; + +/* + * Dsp Io Status codes + */ +enum hda_vendor_status_dspio { + /* Success */ + VENDOR_STATUS_DSPIO_OK = 0x00, + /* Busy, unable to accept new command, the host must retry */ + VENDOR_STATUS_DSPIO_BUSY = 0x01, + /* SCP command queue is full */ + VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02, + /* SCP response queue is empty */ + VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03 +}; + +/* + * Chip Io Status codes + */ +enum hda_vendor_status_chipio { + /* Success */ + VENDOR_STATUS_CHIPIO_OK = 0x00, + /* Busy, unable to accept new command, the host must retry */ + VENDOR_STATUS_CHIPIO_BUSY = 0x01 +}; + +/* + * CA0132 sample rate + */ +enum ca0132_sample_rate { + SR_6_000 = 0x00, + SR_8_000 = 0x01, + SR_9_600 = 0x02, + SR_11_025 = 0x03, + SR_16_000 = 0x04, + SR_22_050 = 0x05, + SR_24_000 = 0x06, + SR_32_000 = 0x07, + SR_44_100 = 0x08, + SR_48_000 = 0x09, + SR_88_200 = 0x0A, + SR_96_000 = 0x0B, + SR_144_000 = 0x0C, + SR_176_400 = 0x0D, + SR_192_000 = 0x0E, + SR_384_000 = 0x0F, + + SR_COUNT = 0x10, + + SR_RATE_UNKNOWN = 0x1F +}; + +/* + * Scp Helper function + */ +enum get_set { + IS_SET = 0, + IS_GET = 1, +}; + +/* + * Duplicated from ca0110 codec + */ + +static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + if (dac) + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); +} + +static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_VREF80); + if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + if (adc) + snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); +} + +static char *dirstr[2] = { "Playback", "Capture" }; + +static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) +#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) +#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) +#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) +#define add_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 0) +#define add_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 0) +#define add_in_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 1) +#define add_in_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 1) + + +/* + * CA0132 specific + */ + +struct ca0132_spec { + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; + hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; + hda_nid_t hp_dac; + hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t adcs[AUTO_PIN_LAST]; + hda_nid_t dig_out; + hda_nid_t dig_in; + unsigned int num_inputs; + long curr_hp_switch; + long curr_hp_volume[2]; + long curr_speaker_switch; + struct mutex chipio_mutex; + const char *input_labels[AUTO_PIN_LAST]; + struct hda_pcm pcm_rec[2]; /* PCM information */ +}; + +/* Chip access helper function */ +static int chipio_send(struct hda_codec *codec, + unsigned int reg, + unsigned int data) +{ + unsigned int res; + int retry = 50; + + /* send bits of data specified by reg */ + do { + res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + reg, data); + if (res == VENDOR_STATUS_CHIPIO_OK) + return 0; + } while (--retry); + return -EIO; +} + +/* + * Write chip address through the vendor widget -- NOT protected by the Mutex! + */ +static int chipio_write_address(struct hda_codec *codec, + unsigned int chip_addx) +{ + int res; + + /* send low 16 bits of the address */ + res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, + chip_addx & 0xffff); + + if (res != -EIO) { + /* send high 16 bits of the address */ + res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, + chip_addx >> 16); + } + + return res; +} + +/* + * Write data through the vendor widget -- NOT protected by the Mutex! + */ + +static int chipio_write_data(struct hda_codec *codec, unsigned int data) +{ + int res; + + /* send low 16 bits of the data */ + res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff); + + if (res != -EIO) { + /* send high 16 bits of the data */ + res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH, + data >> 16); + } + + return res; +} + +/* + * Read data through the vendor widget -- NOT protected by the Mutex! + */ +static int chipio_read_data(struct hda_codec *codec, unsigned int *data) +{ + int res; + + /* post read */ + res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0); + + if (res != -EIO) { + /* read status */ + res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + } + + if (res != -EIO) { + /* read data */ + *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_HIC_READ_DATA, + 0); + } + + return res; +} + +/* + * Write given value to the given address through the chip I/O widget. + * protected by the Mutex + */ +static int chipio_write(struct hda_codec *codec, + unsigned int chip_addx, const unsigned int data) +{ + struct ca0132_spec *spec = codec->spec; + int err; + + mutex_lock(&spec->chipio_mutex); + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_write_data(codec, data); + if (err < 0) + goto exit; + +exit: + mutex_unlock(&spec->chipio_mutex); + return err; +} + +/* + * Read the given address through the chip I/O widget + * protected by the Mutex + */ +static int chipio_read(struct hda_codec *codec, + unsigned int chip_addx, unsigned int *data) +{ + struct ca0132_spec *spec = codec->spec; + int err; + + mutex_lock(&spec->chipio_mutex); + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_read_data(codec, data); + if (err < 0) + goto exit; + +exit: + mutex_unlock(&spec->chipio_mutex); + return err; +} + +/* + * PCM stuffs + */ +static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, + int channel_id, int format) +{ + unsigned int oldval, newval; + + if (!nid) + return; + + snd_printdd("ca0132_setup_stream: " + "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", + nid, stream_tag, channel_id, format); + + /* update the format-id if changed */ + oldval = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_STREAM_FORMAT, + 0); + if (oldval != format) { + msleep(20); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, + format); + } + + oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + newval = (stream_tag << 4) | channel_id; + if (oldval != newval) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, + newval); + } +} + +static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); +} + +/* + * PCM callbacks + */ +static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format); + + return 0; +} + +static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->dacs[0]); + + return 0; +} + +/* + * Digital out + */ +static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format); + + return 0; +} + +static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->dig_out); + + return 0; +} + +/* + * Analog capture + */ +static int ca0132_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 ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->adcs[substream->number], + stream_tag, 0, format); + + return 0; +} + +static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->adcs[substream->number]); + + return 0; +} + +/* + * Digital capture + */ +static int ca0132_dig_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 ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format); + + return 0; +} + +static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->dig_in); + + return 0; +} + +/* + */ +static struct hda_pcm_stream ca0132_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_playback_pcm_prepare, + .cleanup = ca0132_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_capture_pcm_prepare, + .cleanup = ca0132_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_dig_playback_pcm_prepare, + .cleanup = ca0132_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_dig_capture_pcm_prepare, + .cleanup = ca0132_dig_capture_pcm_cleanup + }, +}; + +static int ca0132_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "CA0132 Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + codec->num_pcms++; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info++; + info->name = "CA0132 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0132_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +#define REG_CODEC_MUTE 0x18b014 +#define REG_CODEC_HP_VOL_L 0x18b070 +#define REG_CODEC_HP_VOL_R 0x18b074 + +static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp = spec->curr_hp_switch; + return 0; +} + +static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + unsigned int data; + int err; + + /* any change? */ + if (spec->curr_hp_switch == *valp) + return 0; + + snd_hda_power_up(codec); + + err = chipio_read(codec, REG_CODEC_MUTE, &data); + if (err < 0) + return err; + + /* *valp 0 is mute, 1 is unmute */ + data = (data & 0x7f) | (*valp ? 0 : 0x80); + chipio_write(codec, REG_CODEC_MUTE, data); + if (err < 0) + return err; + + spec->curr_hp_switch = *valp; + + snd_hda_power_down(codec); + return 1; +} + +static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp = spec->curr_speaker_switch; + return 0; +} + +static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + unsigned int data; + int err; + + /* any change? */ + if (spec->curr_speaker_switch == *valp) + return 0; + + snd_hda_power_up(codec); + + err = chipio_read(codec, REG_CODEC_MUTE, &data); + if (err < 0) + return err; + + /* *valp 0 is mute, 1 is unmute */ + data = (data & 0xef) | (*valp ? 0 : 0x10); + chipio_write(codec, REG_CODEC_MUTE, data); + if (err < 0) + return err; + + spec->curr_speaker_switch = *valp; + + snd_hda_power_down(codec); + return 1; +} + +static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp++ = spec->curr_hp_volume[0]; + *valp = spec->curr_hp_volume[1]; + return 0; +} + +static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + long left_vol, right_vol; + unsigned int data; + int val; + int err; + + left_vol = *valp++; + right_vol = *valp; + + /* any change? */ + if ((spec->curr_hp_volume[0] == left_vol) && + (spec->curr_hp_volume[1] == right_vol)) + return 0; + + snd_hda_power_up(codec); + + err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data); + if (err < 0) + return err; + + val = 31 - left_vol; + data = (data & 0xe0) | val; + chipio_write(codec, REG_CODEC_HP_VOL_L, data); + if (err < 0) + return err; + + val = 31 - right_vol; + data = (data & 0xe0) | val; + chipio_write(codec, REG_CODEC_HP_VOL_R, data); + if (err < 0) + return err; + + spec->curr_hp_volume[0] = left_vol; + spec->curr_hp_volume[1] = right_vol; + + snd_hda_power_down(codec); + return 1; +} + +static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Headphone Playback Switch", + nid, 1, 0, HDA_OUTPUT); + knew.get = ca0132_hp_switch_get; + knew.put = ca0132_hp_switch_put; + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO("Headphone Playback Volume", + nid, 3, 0, HDA_OUTPUT); + knew.get = ca0132_hp_volume_get; + knew.put = ca0132_hp_volume_put; + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", + nid, 1, 0, HDA_OUTPUT); + knew.get = ca0132_speaker_switch_get; + knew.put = ca0132_speaker_switch_put; + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static void ca0132_fix_hp_caps(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int caps; + + /* set mute-capable, 1db step, 32 steps, ofs 6 */ + caps = 0x80031f06; + snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps); +} + +static int ca0132_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + if (spec->multiout.num_dacs) { + err = add_speaker_switch(codec, spec->out_pins[0]); + if (err < 0) + return err; + } + + if (cfg->hp_outs) { + ca0132_fix_hp_caps(codec); + err = add_hp_switch(codec, cfg->hp_pins[0]); + if (err < 0) + return err; + err = add_hp_volume(codec, cfg->hp_pins[0]); + if (err < 0) + return err; + } + + for (i = 0; i < spec->num_inputs; i++) { + const char *label = spec->input_labels[i]; + + err = add_in_switch(codec, spec->adcs[i], label); + if (err < 0) + return err; + err = add_in_volume(codec, spec->adcs[i], label); + if (err < 0) + return err; + if (cfg->inputs[i].type == AUTO_PIN_MIC) { + /* add Mic-Boost */ + err = add_in_mono_volume(codec, spec->input_pins[i], + "Mic Boost", 1); + if (err < 0) + return err; + } + } + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); + if (err < 0) + return err; + err = add_out_volume(codec, spec->dig_out, "IEC958"); + if (err < 0) + return err; + } + + if (spec->dig_in) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + err = add_in_volume(codec, spec->dig_in, "IEC958"); + } + return 0; +} + + +static void ca0132_set_ct_ext(struct hda_codec *codec, int enable) +{ + /* Set Creative extension */ + snd_printdd("SET CREATIVE EXTENSION\n"); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, + enable); + msleep(20); +} + + +static void ca0132_config(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + /* line-outs */ + cfg->line_outs = 1; + cfg->line_out_pins[0] = 0x0b; /* front */ + cfg->line_out_type = AUTO_PIN_LINE_OUT; + + spec->dacs[0] = 0x02; + spec->out_pins[0] = 0x0b; + spec->multiout.dac_nids = spec->dacs; + spec->multiout.num_dacs = 1; + spec->multiout.max_channels = 2; + + /* headphone */ + cfg->hp_outs = 1; + cfg->hp_pins[0] = 0x0f; + + spec->hp_dac = 0; + spec->multiout.hp_nid = 0; + + /* inputs */ + cfg->num_inputs = 2; /* Mic-in and line-in */ + cfg->inputs[0].pin = 0x12; + cfg->inputs[0].type = AUTO_PIN_MIC; + cfg->inputs[1].pin = 0x11; + cfg->inputs[1].type = AUTO_PIN_LINE_IN; + + /* Mic-in */ + spec->input_pins[0] = 0x12; + spec->input_labels[0] = "Mic-In"; + spec->adcs[0] = 0x07; + + /* Line-In */ + spec->input_pins[1] = 0x11; + spec->input_labels[1] = "Line-In"; + spec->adcs[1] = 0x08; + spec->num_inputs = 2; +} + +static void ca0132_init_chip(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_init(&spec->chipio_mutex); +} + +static void ca0132_exit_chip(struct hda_codec *codec) +{ + /* put any chip cleanup stuffs here. */ +} + +static int ca0132_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + init_output(codec, spec->out_pins[i], + spec->multiout.dac_nids[i]); + } + init_output(codec, cfg->hp_pins[0], spec->hp_dac); + init_output(codec, cfg->dig_out_pins[0], spec->dig_out); + + for (i = 0; i < spec->num_inputs; i++) + init_input(codec, spec->input_pins[i], spec->adcs[i]); + + init_input(codec, cfg->dig_in_pin, spec->dig_in); + + ca0132_set_ct_ext(codec, 1); + + return 0; +} + + +static void ca0132_free(struct hda_codec *codec) +{ + ca0132_set_ct_ext(codec, 0); + ca0132_exit_chip(codec); + kfree(codec->spec); +} + +static struct hda_codec_ops ca0132_patch_ops = { + .build_controls = ca0132_build_controls, + .build_pcms = ca0132_build_pcms, + .init = ca0132_init, + .free = ca0132_free, +}; + + + +static int patch_ca0132(struct hda_codec *codec) +{ + struct ca0132_spec *spec; + + snd_printdd("patch_ca0132\n"); + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + ca0132_init_chip(codec); + + ca0132_config(codec); + + codec->patch_ops = ca0132_patch_ops; + + return 0; +} + +/* + * patch entries + */ +static struct hda_codec_preset snd_hda_preset_ca0132[] = { + { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 }, + {} /* terminator */ +}; + +MODULE_ALIAS("snd-hda-codec-id:11020011"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec"); + +static struct hda_codec_preset_list ca0132_list = { + .preset = snd_hda_preset_ca0132, + .owner = THIS_MODULE, +}; + +static int __init patch_ca0132_init(void) +{ + return snd_hda_add_codec_preset(&ca0132_list); +} + +static void __exit patch_ca0132_exit(void) +{ + snd_hda_delete_codec_preset(&ca0132_list); +} + +module_init(patch_ca0132_init) +module_exit(patch_ca0132_exit) -- cgit v1.2.3 From efb9f469b6f563a9e54cc67575d38032800a49f2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 07:44:51 +0200 Subject: ALSA: hda - Fix a compile error in patch_ca0132.c for the recent SPDIF change Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 55f58e76cce7..d9a2254ceef6 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -921,7 +921,8 @@ static int ca0132_build_controls(struct hda_codec *codec) } if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); if (err < 0) return err; err = add_out_volume(codec, spec->dig_out, "IEC958"); -- cgit v1.2.3 From 30f7c5d491ec2d515148882fa0b4080ab61d4bb0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 08:37:41 +0200 Subject: ALSA: hda - Use xxx Boost Volume for VIA Drop "Capture" prefix from the mic-boost names. Otherwise some control names can overflow the max name length. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 819267a4e2df..51e7ce010543 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1947,7 +1947,7 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) continue; label = hda_get_autocfg_input_label(codec, cfg, i); - snprintf(name, sizeof(name), "%s Boost Capture Volume", label); + snprintf(name, sizeof(name), "%s Boost Volume", label); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); if (err < 0) -- cgit v1.2.3 From 8e3679dca200a326426a92d998b63cab5a17c52d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 09:01:36 +0200 Subject: ALSA: hda - Revisit output_path parsing in patch_via.c Change the order of the output-path list in a way from the DAC to the target pin. Also now the list include the target pin, too. Together with this format change, simplify the arguments of parse_output_path() function, and fix the initialization in via_auto_init_output(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 87 +++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 51e7ce010543..e445a4d24778 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -81,10 +81,12 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) +#define MAX_NID_PATH_DEPTH 5 + struct nid_path { int depth; - hda_nid_t path[5]; - short idx[5]; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + short idx[MAX_NID_PATH_DEPTH]; }; struct via_spec { @@ -415,15 +417,22 @@ static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, return; /* select the route explicitly when multiple connections exist */ - if (num_conns > 1) + if (num_conns > 1 && + get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); + /* unmute if the input amp is present */ - if (!(query_amp_caps(codec, nid, HDA_INPUT) & - (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(idx)); + if (query_amp_caps(codec, nid, HDA_INPUT) & + (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(idx)); + + /* unmute the src output */ + if (query_amp_caps(codec, src, HDA_OUTPUT) & + (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) + snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); /* unmute AA-path if present */ if (!mix) @@ -469,15 +478,9 @@ static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, } /* initialize the output path */ - nid = pin; - for (i = 0; i < path->depth; i++) { - unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid); - nid = path->path[i]; - if (query_amp_caps(codec, nid, HDA_OUTPUT) & - (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + for (i = path->depth - 1; i > 0; i--) { + nid = path->path[i - 1]; + unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid); } } @@ -1544,7 +1547,7 @@ static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) return true; } -static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, +static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, hda_nid_t target_dac, struct nid_path *path, int depth, int wid_type) { @@ -1556,13 +1559,13 @@ static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) continue; if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { - path->path[depth] = conn[i]; - path->idx[depth] = i; - path->depth = ++depth; + path->path[0] = conn[i]; + path->idx[0] = i; + path->depth = 1; return true; } } - if (depth > 4) + if (depth >= MAX_NID_PATH_DEPTH) return false; for (i = 0; i < nums; i++) { unsigned int type; @@ -1570,16 +1573,28 @@ static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, if (type == AC_WID_AUD_OUT || (wid_type != -1 && type != wid_type)) continue; - if (parse_output_path(codec, conn[i], target_dac, + if (__parse_output_path(codec, conn[i], target_dac, path, depth + 1, AC_WID_AUD_SEL)) { - path->path[depth] = conn[i]; - path->idx[depth] = i; + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + path->depth++; return true; } } return false; } +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, struct nid_path *path) +{ + if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) { + path->path[path->depth] = nid; + path->depth++; + return true; + } + return false; +} + static int via_auto_fill_dac_nids(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1593,9 +1608,8 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec) nid = cfg->line_out_pins[i]; if (!nid) continue; - if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) - spec->private_dac_nids[i] = - spec->out_path[i].path[spec->out_path[i].depth - 1]; + if (parse_output_path(codec, nid, 0, &spec->out_path[i])) + spec->private_dac_nids[i] = spec->out_path[i].path[0]; } return 0; } @@ -1748,15 +1762,14 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) if (!pin) return 0; - if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { - spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1]; - spec->hp_independent_mode_index = - spec->hp_path.idx[spec->hp_path.depth - 1]; + if (parse_output_path(codec, pin, 0, &spec->hp_path)) { + spec->hp_dac_nid = spec->hp_path.path[0]; + spec->hp_independent_mode_index = spec->hp_path.idx[0]; create_hp_imux(spec); } if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - &spec->hp_dep_path, 0, -1) && + &spec->hp_dep_path) && !spec->hp_dac_nid) return 0; @@ -1777,14 +1790,14 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec) if (!spec->autocfg.speaker_outs || !pin) return 0; - if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) { - dac = spec->speaker_path.path[spec->speaker_path.depth - 1]; + if (parse_output_path(codec, pin, 0, &spec->speaker_path)) { + dac = spec->speaker_path.path[0]; spec->multiout.extra_out_nid[0] = dac; return create_ch_ctls(codec, "Speaker", pin, dac, 3); } if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - &spec->speaker_path, 0, -1)) - return create_ch_ctls(codec, "Headphone", pin, 0, 3); + &spec->speaker_path)) + return create_ch_ctls(codec, "Speaker", pin, 0, 3); return 0; } -- cgit v1.2.3 From a00a5fad9ddbabc7cd03d143520b9a4730edc75d Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 21 Jun 2011 16:11:11 +0800 Subject: ALSA: hda - Fix creations of playback volume controls in patch_via.c Fix a issue to create playback volume control if pin has amplifier capability but not DAC. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e445a4d24778..853d24411d53 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1631,7 +1631,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, if (nid) { sprintf(name, "%s Playback Volume", pfx); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; } -- cgit v1.2.3 From 8df2a3129d946dc91f9824958567a990329822b3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 11:48:29 +0200 Subject: ALSA: hda - Fix re-routing of HP-independent mode in patch_via.c Re-route the whole output path when HP-independent mode is changed. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 101 +++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 853d24411d53..7b353405e068 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -138,9 +138,7 @@ struct via_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; /* HP mode source */ - const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; - unsigned int hp_independent_mode_index; unsigned int dmic_enabled; unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; @@ -406,6 +404,24 @@ static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, #define get_connection_index(codec, mux, nid) \ __get_connection_index(codec, mux, nid, NULL) +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int mask) +{ + unsigned int caps = get_wcaps(codec, nid); + if (dir == HDA_INPUT) + caps &= AC_WCAP_IN_AMP; + else + caps &= AC_WCAP_OUT_AMP; + if (!caps) + return false; + if (query_amp_caps(codec, nid, dir) & mask) + return true; + return false; +} + +#define have_vol_or_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE) + /* unmute input amp and select the specificed source */ static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, hda_nid_t src, hda_nid_t mix) @@ -423,22 +439,20 @@ static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, AC_VERB_SET_CONNECT_SEL, idx); /* unmute if the input amp is present */ - if (query_amp_caps(codec, nid, HDA_INPUT) & - (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) + if (have_vol_or_mute(codec, nid, HDA_INPUT)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(idx)); /* unmute the src output */ - if (query_amp_caps(codec, src, HDA_OUTPUT) & - (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) + if (have_vol_or_mute(codec, src, HDA_OUTPUT)) snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); /* unmute AA-path if present */ - if (!mix) + if (!mix || mix == src) return; idx = __get_connection_index(codec, nid, mix, NULL); - if (idx >= 0) + if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(idx)); @@ -694,9 +708,16 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, static int via_independent_hp_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->hp_mux, uinfo); + static const char * const texts[] = { "OFF", "ON" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; } static int via_independent_hp_get(struct snd_kcontrol *kcontrol, @@ -714,12 +735,28 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - unsigned int pinsel = ucontrol->value.enumerated.item[0]; - /* Get Independent Mode index of headphone pin widget */ - spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel - ? 1 : 0; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); + hda_nid_t nid, src; + int i, idx, num_conns; + struct nid_path *path; + + spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0]; + if (spec->hp_independent_mode) + path = &spec->hp_path; + else + path = &spec->hp_dep_path; + + /* re-route the output path */ + for (i = path->depth - 1; i > 0; i--) { + nid = path->path[i]; + src = path->path[i - 1]; + idx = __get_connection_index(codec, nid, src, &num_conns); + if (idx < 0) + continue; + if (num_conns > 1 && + get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, idx); + } /* update jack power state */ set_widgets_power_state(codec); @@ -746,7 +783,6 @@ static int via_hp_build(struct hda_codec *codec) return -ENOMEM; knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - knew->private_value = nid; return 0; } @@ -1622,9 +1658,9 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, hda_nid_t nid; int err; - if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) nid = dac; - else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) nid = pin; else nid = 0; @@ -1636,9 +1672,9 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, return err; } - if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) nid = dac; - else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) nid = pin; else nid = 0; @@ -1741,19 +1777,6 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) return 0; } -static void create_hp_imux(struct via_spec *spec) -{ - int i; - struct hda_input_mux *imux = &spec->private_imux[1]; - static const char * const texts[] = { "OFF", "ON", NULL}; - - /* for hp mode select */ - for (i = 0; texts[i]; i++) - snd_hda_add_imux_item(imux, texts[i], i, NULL); - - spec->hp_mux = &spec->private_imux[1]; -} - static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; @@ -1762,18 +1785,14 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) if (!pin) return 0; - if (parse_output_path(codec, pin, 0, &spec->hp_path)) { + if (parse_output_path(codec, pin, 0, &spec->hp_path)) spec->hp_dac_nid = spec->hp_path.path[0]; - spec->hp_independent_mode_index = spec->hp_path.idx[0]; - create_hp_imux(spec); - } if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], &spec->hp_dep_path) && !spec->hp_dac_nid) return 0; - err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); if (err < 0) return err; @@ -2068,7 +2087,7 @@ static int via_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; - if (spec->hp_mux) { + if (spec->hp_dac_nid && spec->hp_dep_path.depth) { err = via_hp_build(codec); if (err < 0) return err; -- cgit v1.2.3 From 0f98c24b807f024d42cf743897e2c1d95ff1e8be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 12:51:33 +0200 Subject: ALSA: hda - Assign smart51 only in the same stack for VIA codecs The input jacks assigned as the smart51 outputs must be in the same stack, either rear, front or other. Also, prefer line-in as the surround to mic-in. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 63 ++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 7b353405e068..785f7f5022a4 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -822,26 +822,6 @@ static void mute_aa_path(struct hda_codec *codec, int mute) } } -static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int defcfg; - if (pin != cfg->inputs[i].pin) - continue; - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return false; - defcfg = snd_hda_codec_get_pincfg(codec, pin); - if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) - return false; - return true; - } - return false; -} - static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; @@ -1692,21 +1672,38 @@ static void mangle_smart51(struct hda_codec *codec) { struct via_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int i, nums = 0; - - for (i = 0; i < cfg->num_inputs; i++) { - if (is_smart51_candidate(codec, cfg->inputs[i].pin)) + struct auto_pin_cfg_item *ins = cfg->inputs; + int i, j, nums, attr; + int pins[AUTO_CFG_MAX_INS]; + + for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { + nums = 0; + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int def; + if (ins[i].type > AUTO_PIN_LINE_IN) + continue; + def = snd_hda_codec_get_pincfg(codec, ins[i].pin); + if (snd_hda_get_input_pin_attr(def) != attr) + continue; + for (j = 0; j < nums; j++) + if (ins[pins[j]].type < ins[i].type) { + memmove(pins + j + 1, pins + j, + (nums - j - 1) * sizeof(int)); + break; + } + pins[j] = i; nums++; - } - if (cfg->line_outs + nums < 3) - return; - for (i = 0; i < cfg->num_inputs; i++) { - if (!is_smart51_candidate(codec, cfg->inputs[i].pin)) + } + if (cfg->line_outs + nums < 3) continue; - spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin; - cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; - if (cfg->line_outs == 3) - break; + for (i = 0; i < nums; i++) { + hda_nid_t pin = ins[pins[i]].pin; + spec->smart51_pins[spec->smart51_nums++] = pin; + cfg->line_out_pins[cfg->line_outs++] = pin; + if (cfg->line_outs == 3) + break; + } + return; } } -- cgit v1.2.3 From 1e11cae143e4c0a4fc77fe532e18c550d63ab02d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 12:57:22 +0200 Subject: ALSA: hda - Fix the check of loopback-mixer element index in patch_via.c Fix the check of the multiple loopback-mixer, which gave sometimes a wrong index assigned to an element even for different names, e.g. Mic and Front Mic. Now check the label properly for avoid duplication. Reported-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 785f7f5022a4..b67a5768a9de 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1875,6 +1875,7 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; int i, j, err, idx, idx2, type, type_idx = 0; + const char *prev_label = NULL; hda_nid_t cap_nid; hda_nid_t pin_idxs[8]; int num_idxs; @@ -1908,11 +1909,12 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, break; if (idx >= num_idxs) continue; - if (i > 0 && type == cfg->inputs[i - 1].type) + label = hda_get_autocfg_input_label(codec, cfg, i); + if (prev_label && !strcmp(label, prev_label)) type_idx++; else type_idx = 0; - label = hda_get_autocfg_input_label(codec, cfg, i); + prev_label = label; idx2 = get_connection_index(codec, spec->aa_mix_nid, pin_idxs[idx]); if (idx2 >= 0) { -- cgit v1.2.3 From a934d5a983528543850c90b29bedbdfd71f7097b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 14:22:14 +0200 Subject: ALSA: hda - Fix surround-volume parsing for VT1708B codecs The surround/CLFE/side DACs on VT1708B and co have no amp but the connected selector widgets have the amp instead. Fix the parser to check these selector widgets for the possible mixer controls as well. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b67a5768a9de..5b907b356951 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -407,7 +407,10 @@ static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int mask) { - unsigned int caps = get_wcaps(codec, nid); + unsigned int caps; + if (!nid) + return false; + caps = get_wcaps(codec, nid); if (dir == HDA_INPUT) caps &= AC_WCAP_IN_AMP; else @@ -1635,13 +1638,21 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, { struct via_spec *spec = codec->spec; char name[32]; - hda_nid_t nid; - int err; + hda_nid_t nid, sel, conn[8]; + int nums, err; + + /* check selector widget connected to the pin */ + sel = 0; + nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn)); + if (nums == 1 && conn[0] != pin) + sel = conn[0]; if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) nid = dac; else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = sel; else nid = 0; if (nid) { @@ -1656,6 +1667,8 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, nid = dac; else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = sel; else nid = 0; if (nid) { -- cgit v1.2.3 From 09a9ad69a5467fbda3fd358d2be155c22aa416e4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 15:57:44 +0200 Subject: ALSA: hda - VT1708 independent HP routing fix The codecs like VT1708 needs more complicated routing using the mixer widget rather than the simple selector widgets. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 222 +++++++++++++++++++++++++--------------------- 1 file changed, 122 insertions(+), 100 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5b907b356951..bceb6b2364fe 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -83,10 +83,20 @@ enum VIA_HDA_CODEC { #define MAX_NID_PATH_DEPTH 5 +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ struct nid_path { int depth; hda_nid_t path[MAX_NID_PATH_DEPTH]; - short idx[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int vol_ctl; + unsigned int mute_ctl; }; struct via_spec { @@ -422,43 +432,39 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, return false; } -#define have_vol_or_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE) +#define have_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -/* unmute input amp and select the specificed source */ -static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t src, hda_nid_t mix) +/* enable/disable the output-route */ +static void activate_output_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool force) { - int idx, num_conns; - - idx = __get_connection_index(codec, nid, src, &num_conns); - if (idx < 0) - return; - - /* select the route explicitly when multiple connections exist */ - if (num_conns > 1 && - get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, idx); - - /* unmute if the input amp is present */ - if (have_vol_or_mute(codec, nid, HDA_INPUT)) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(idx)); - - /* unmute the src output */ - if (have_vol_or_mute(codec, src, HDA_OUTPUT)) - snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - - /* unmute AA-path if present */ - if (!mix || mix == src) - return; - idx = __get_connection_index(codec, nid, mix, NULL); - if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(idx)); + int i; + for (i = 0; i < path->depth; i++) { + hda_nid_t src, dst; + int idx = path->idx[i]; + src = path->path[i]; + if (i < path->depth - 1) + dst = path->path[i + 1]; + else + dst = 0; + if (enable && path->multi[i]) + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_CONNECT_SEL, idx); + if (have_mute(codec, dst, HDA_INPUT)) { + int val = enable ? AMP_IN_UNMUTE(idx) : + AMP_IN_MUTE(idx); + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + if (!force && (src == path->vol_ctl || src == path->mute_ctl)) + continue; + if (have_mute(codec, src, HDA_OUTPUT)) { + int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; + snd_hda_codec_write(codec, src, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + } } /* set the given pin as output */ @@ -474,16 +480,18 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } -static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, - int pin_type, struct nid_path *path) +static void via_auto_init_output(struct hda_codec *codec, + struct nid_path *path, int pin_type, + bool force) { struct via_spec *spec = codec->spec; unsigned int caps; - hda_nid_t nid; - int i; + hda_nid_t pin, nid; + int i, idx; - if (!pin) + if (!path->depth) return; + pin = path->path[path->depth - 1]; init_output_pin(codec, pin, pin_type); caps = query_amp_caps(codec, pin, HDA_OUTPUT); @@ -494,34 +502,48 @@ static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, AMP_OUT_MUTE | val); } - /* initialize the output path */ + activate_output_path(codec, path, true, force); + + /* initialize the AA-path */ + if (!spec->aa_mix_nid) + return; for (i = path->depth - 1; i > 0; i--) { - nid = path->path[i - 1]; - unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid); + nid = path->path[i]; + idx = get_connection_index(codec, nid, spec->aa_mix_nid); + if (idx >= 0) { + if (have_mute(codec, nid, HDA_INPUT)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(idx)); + break; + } } } - static void via_auto_init_multi_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int i; for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) - via_auto_init_output(codec, spec->autocfg.line_out_pins[i], - PIN_OUT, &spec->out_path[i]); + via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true); } static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - if (spec->hp_dac_nid) - via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, - &spec->hp_path); - else - via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, - &spec->hp_dep_path); + if (!spec->hp_dac_nid) { + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); + return; + } + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_dep_path, false, false); + via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); + } else { + activate_output_path(codec, &spec->hp_path, false, false); + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); + } } static void via_auto_init_speaker_out(struct hda_codec *codec) @@ -529,8 +551,7 @@ static void via_auto_init_speaker_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (spec->autocfg.speaker_outs) - via_auto_init_output(codec, spec->autocfg.speaker_pins[0], - PIN_OUT, &spec->speaker_path); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true); } static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); @@ -738,27 +759,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid, src; - int i, idx, num_conns; - struct nid_path *path; spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0]; - if (spec->hp_independent_mode) - path = &spec->hp_path; - else - path = &spec->hp_dep_path; - - /* re-route the output path */ - for (i = path->depth - 1; i > 0; i--) { - nid = path->path[i]; - src = path->path[i - 1]; - idx = __get_connection_index(codec, nid, src, &num_conns); - if (idx < 0) - continue; - if (num_conns > 1 && - get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, idx); + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_dep_path, false, false); + activate_output_path(codec, &spec->hp_path, true, false); + } else { + activate_output_path(codec, &spec->hp_path, false, false); + activate_output_path(codec, &spec->hp_dep_path, true, false); } /* update jack power state */ @@ -1577,12 +1585,8 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < nums; i++) { if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) continue; - if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { - path->path[0] = conn[i]; - path->idx[0] = i; - path->depth = 1; - return true; - } + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) + goto found; } if (depth >= MAX_NID_PATH_DEPTH) return false; @@ -1593,14 +1597,18 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, (wid_type != -1 && type != wid_type)) continue; if (__parse_output_path(codec, conn[i], target_dac, - path, depth + 1, AC_WID_AUD_SEL)) { - path->path[path->depth] = conn[i]; - path->idx[path->depth] = i; - path->depth++; - return true; - } + path, depth + 1, AC_WID_AUD_SEL)) + goto found; } return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; } static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, @@ -1634,18 +1642,16 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec) } static int create_ch_ctls(struct hda_codec *codec, const char *pfx, - hda_nid_t pin, hda_nid_t dac, int chs) + int chs, bool check_dac, struct nid_path *path) { struct via_spec *spec = codec->spec; char name[32]; - hda_nid_t nid, sel, conn[8]; - int nums, err; + hda_nid_t dac, pin, sel, nid; + int err; - /* check selector widget connected to the pin */ - sel = 0; - nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn)); - if (nums == 1 && conn[0] != pin) - sel = conn[0]; + dac = check_dac ? path->path[0] : 0; + pin = path->path[path->depth - 1]; + sel = path->depth > 1 ? path->path[1] : 0; if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) nid = dac; @@ -1661,6 +1667,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; + path->vol_ctl = nid; } if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) @@ -1677,6 +1684,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; + path->mute_ctl = nid; } return 0; } @@ -1747,10 +1755,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (!pin || !dac) continue; if (i == HDA_CLFE) { - err = create_ch_ctls(codec, "Center", pin, dac, 1); + err = create_ch_ctls(codec, "Center", 1, true, + &spec->out_path[i]); if (err < 0) return err; - err = create_ch_ctls(codec, "LFE", pin, dac, 2); + err = create_ch_ctls(codec, "LFE", 2, true, + &spec->out_path[i]); if (err < 0) return err; } else { @@ -1758,7 +1768,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->line_outs == 1) pfx = "Speaker"; - err = create_ch_ctls(codec, pfx, pin, dac, 3); + err = create_ch_ctls(codec, pfx, 3, true, + &spec->out_path[i]); if (err < 0) return err; } @@ -1790,6 +1801,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; + struct nid_path *path; int err; if (!pin) @@ -1803,9 +1815,17 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) !spec->hp_dac_nid) return 0; - err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); + if (spec->hp_dac_nid) + path = &spec->hp_path; + else + path = &spec->hp_dep_path; + err = create_ch_ctls(codec, "Headphone", 3, false, path); if (err < 0) return err; + if (spec->hp_dac_nid) { + spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl; + spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl; + } return 0; } @@ -1822,11 +1842,13 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec) if (parse_output_path(codec, pin, 0, &spec->speaker_path)) { dac = spec->speaker_path.path[0]; spec->multiout.extra_out_nid[0] = dac; - return create_ch_ctls(codec, "Speaker", pin, dac, 3); + return create_ch_ctls(codec, "Speaker", 3, true, + &spec->speaker_path); } if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], &spec->speaker_path)) - return create_ch_ctls(codec, "Speaker", pin, 0, 3); + return create_ch_ctls(codec, "Speaker", 3, false, + &spec->speaker_path); return 0; } -- cgit v1.2.3 From ddd304d8be4ffbb3662a92da515b1c74376b2280 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 16:33:55 +0200 Subject: ALSA: hda - Remove redundant VT1709 and VT1708B codes Unify the VT1709 10ch and 6ch parsers, as well as VT1708B 8ch and 4ch parsers. They have no difference now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 86 +++++++++++------------------------------------ 1 file changed, 19 insertions(+), 67 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index bceb6b2364fe..899b96631312 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2291,32 +2291,7 @@ static int patch_vt1708(struct hda_codec *codec) return 0; } -static int patch_vt1709_10ch(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->aa_mix_nid = 0x18; - - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - codec->patch_ops = via_patch_ops; - - return 0; -} -/* - * generic initialization of ADC, input mixers and output mixers - */ -static int patch_vt1709_6ch(struct hda_codec *codec) +static int patch_vt1709(struct hda_codec *codec) { struct via_spec *spec; int err; @@ -2420,13 +2395,14 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) } static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B_8ch(struct hda_codec *codec) +static int patch_vt1708B(struct hda_codec *codec) { struct via_spec *spec; int err; if (get_codec_type(codec) == VT1708BCE) return patch_vt1708S(codec); + /* create a codec specific record */ spec = via_new_spec(codec); if (spec == NULL) @@ -2448,30 +2424,6 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) return 0; } -static int patch_vt1708B_4ch(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; - - return 0; -} - /* Patch for VT1708S */ static const struct hda_verb vt1708S_init_verbs[] = { /* Enable Mic Boost Volume backdoor */ @@ -3275,37 +3227,37 @@ static const struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106e710, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e711, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e712, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e713, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e714, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e715, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e716, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e717, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e720, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e721, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e722, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e723, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e724, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e725, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e726, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e727, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x11060397, .name = "VT1708S", .patch = patch_vt1708S}, { .id = 0x11061397, .name = "VT1708S", -- cgit v1.2.3 From f2b1c9f031d6b7604f861223f9e7024e6597b201 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Jun 2011 16:52:39 +0200 Subject: ALSA: hda - Auto-mute smart51 surround pins for VIA codecs When smart51 mode is enabled, auto-mute these surround outputs as well as the primary line-out. Also this patch includes minor clean-ups. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 899b96631312..af47b9aca974 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -844,33 +844,13 @@ static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) return false; } -static int via_smart51_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - static int via_smart51_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - int on = 1; - int i; - for (i = 0; i < spec->smart51_nums; i++) { - hda_nid_t nid = spec->smart51_pins[i]; - unsigned int ctl; - ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) - on = 0; - } - *ucontrol->value.integer.value = on; + *ucontrol->value.integer.value = spec->smart51_enabled; return 0; } @@ -908,7 +888,7 @@ static const struct snd_kcontrol_new via_smart51_mixer = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Smart 5.1", .count = 1, - .info = via_smart51_info, + .info = snd_ctl_boolean_mono_info, .get = via_smart51_get, .put = via_smart51_put, }; @@ -1450,8 +1430,13 @@ static void via_hp_automute(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { + int nums; present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - toggle_output_mutes(codec, spec->autocfg.line_outs, + if (spec->smart51_enabled) + nums = spec->autocfg.line_outs + spec->smart51_nums; + else + nums = spec->autocfg.line_outs; + toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); } -- cgit v1.2.3 From a86a88eaf6db7bcc3900d0b7d4755474cc73201f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Jun 2011 15:23:25 +0200 Subject: ALSA: hda - Implement dynamic-ADC switching for VIA codecs Some VIA codecs like VT1702 provide the input-route only to specific ADCs such as digital-mic inputs. These routes aren't covered by the normal primary ADC, and for now, user had to open the capture stream assigned to that special ADC manually for using such inputs. This patch implements a way to switch the current ADC dynamically per the input-source selection in such a case. When this workaround is activated, the driver provides only one capture stream and one input- source control but with the full possible inputs. The driver switches the ADC to be used (or being used) according to the input-source on the fly. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 516 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 391 insertions(+), 125 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index af47b9aca974..fb5468b4c55a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -99,6 +99,14 @@ struct nid_path { unsigned int mute_ctl; }; +/* input-path */ +struct via_input { + hda_nid_t pin; /* input-pin or aa-mix */ + int adc_idx; /* ADC index to be used */ + int mux_idx; /* MUX index (if any) */ + const char *label; /* input-source label */ +}; + struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; @@ -135,16 +143,22 @@ struct via_spec { hda_nid_t dig_in_nid; /* capture source */ - const struct hda_input_mux *input_mux; + bool dyn_adc_switch; + int num_inputs; + struct via_input inputs[AUTO_CFG_MAX_INS + 1]; unsigned int cur_mux[3]; + /* dynamic ADC switching */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + /* PCM information */ struct hda_pcm pcm_rec[3]; /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; struct snd_array kctls; - struct hda_input_mux private_imux[2]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; /* HP mode source */ @@ -171,6 +185,10 @@ struct via_spec { struct hda_loopback_check loopback; int num_loopbacks; struct hda_amp_list loopback_list[8]; + + /* bind capture-volume */ + struct hda_bind_ctls *bind_cap_vol; + struct hda_bind_ctls *bind_cap_sw; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -586,12 +604,15 @@ static void via_auto_init_analog_input(struct hda_codec *codec) /* init input-src */ for (i = 0; i < spec->num_adc_nids; i++) { - const struct hda_input_mux *imux = spec->input_mux; - if (!imux || !spec->mux_nids[i]) - continue; - snd_hda_codec_write(codec, spec->mux_nids[i], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[spec->cur_mux[i]].index); + int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; + if (spec->mux_nids[adc_idx]) { + int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + } + if (spec->dyn_adc_switch) + break; /* only one input-src */ } /* init aa-mixer */ @@ -682,53 +703,6 @@ static const struct snd_kcontrol_new via_pin_power_ctl_enum = { }; -/* - * input MUX handling - */ -static int via_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int via_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int via_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - int ret; - - if (!spec->mux_nids[adc_idx]) - return -EINVAL; - /* switch to D0 beofre change index */ - if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) - snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->mux_nids[adc_idx], - &spec->cur_mux[adc_idx]); - /* update jack power state */ - set_widgets_power_state(codec); - - return ret; -} - static int via_independent_hp_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1149,6 +1123,53 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } +/* analog capture with dynamic ADC switching */ +static int via_dyn_adc_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 via_spec *spec = codec->spec; + int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; + + spec->cur_adc = spec->adc_nids[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 via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +/* re-setup the stream if running; called from input-src put */ +static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[cur].adc_idx; + hda_nid_t adc = spec->adc_nids[adc_idx]; + + if (spec->cur_adc && spec->cur_adc != adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = adc; + snd_hda_codec_setup_stream(codec, adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + return true; + } + return false; +} + static const struct hda_pcm_stream via_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -1204,6 +1225,17 @@ static const struct hda_pcm_stream via_pcm_analog_capture = { }, }; +static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .prepare = via_dyn_adc_capture_pcm_prepare, + .cleanup = via_dyn_adc_capture_pcm_cleanup, + }, +}; + static const struct hda_pcm_stream via_pcm_digital_playback = { .substreams = 1, .channels_min = 2, @@ -1336,13 +1368,19 @@ static int via_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; - if (!spec->stream_analog_capture) - spec->stream_analog_capture = &via_pcm_analog_capture; + if (!spec->stream_analog_capture) { + if (spec->dyn_adc_switch) + spec->stream_analog_capture = + &via_pcm_dyn_adc_analog_capture; + else + spec->stream_analog_capture = &via_pcm_analog_capture; + } info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->stream_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids; + if (!spec->dyn_adc_switch) + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids; if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; @@ -1394,7 +1432,9 @@ static void via_free(struct hda_codec *codec) via_free_kctls(codec); vt1708_stop_hp_work(spec); - kfree(codec->spec); + kfree(spec->bind_cap_vol); + kfree(spec->bind_cap_sw); + kfree(spec); } /* mute/unmute outputs */ @@ -1860,7 +1900,74 @@ static int via_fill_adcs(struct hda_codec *codec) return 0; } -static int get_mux_nids(struct hda_codec *codec); +/* input-src control */ +static int via_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + strcpy(uinfo->value.enumerated.name, + spec->inputs[uinfo->value.enumerated.item].label); + return 0; +} + +static int via_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; + return 0; +} + +static int via_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + hda_nid_t mux; + int cur; + + cur = ucontrol->value.enumerated.item[0]; + if (cur < 0 || cur >= spec->num_inputs) + return -EINVAL; + if (spec->cur_mux[idx] == cur) + return 0; + spec->cur_mux[idx] = cur; + if (spec->dyn_adc_switch) { + int adc_idx = spec->inputs[cur].adc_idx; + mux = spec->mux_nids[adc_idx]; + via_dyn_adc_pcm_resetup(codec, cur); + } else { + mux = spec->mux_nids[idx]; + if (snd_BUG_ON(!mux)) + return -EINVAL; + } + + if (mux) { + /* switch to D0 beofre change index */ + if (snd_hda_codec_read(codec, mux, 0, + AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, + spec->inputs[cur].mux_idx); + } + + /* update jack power state */ + set_widgets_power_state(codec); + return 0; +} static const struct snd_kcontrol_new via_input_src_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1874,6 +1981,22 @@ static const struct snd_kcontrol_new via_input_src_ctl = { .put = via_mux_enum_put, }; +static int create_input_src_ctls(struct hda_codec *codec, int count) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (spec->num_inputs <= 1 || !count) + return 0; /* no need for single src */ + + knew = via_clone_control(spec, &via_input_src_ctl); + if (!knew) + return -ENOMEM; + knew->count = count; + return 0; +} + +/* add the powersave loopback-list entry */ static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) { struct hda_amp_list *list; @@ -1888,17 +2011,65 @@ static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) spec->loopback.amplist = spec->loopback_list; } -/* create playback/capture controls for input pins */ -static int via_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) +/* check whether the path from src to dst is reachable */ +static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, + hda_nid_t dst, int depth) +{ + hda_nid_t conn[8]; + int i, nums; + + nums = snd_hda_get_connections(codec, src, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == dst) + return true; + if (++depth > MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) + if (is_reachable_nid(codec, conn[i], dst, depth)) + return true; + return false; +} + +/* add the input-route to the given pin */ +static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, j, err, idx, idx2, type, type_idx = 0; - const char *prev_label = NULL; - hda_nid_t cap_nid; - hda_nid_t pin_idxs[8]; - int num_idxs; + int c, idx; + + spec->inputs[spec->num_inputs].adc_idx = -1; + spec->inputs[spec->num_inputs].pin = pin; + for (c = 0; c < spec->num_adc_nids; c++) { + if (spec->mux_nids[c]) { + idx = get_connection_index(codec, spec->mux_nids[c], + pin); + if (idx < 0) + continue; + spec->inputs[spec->num_inputs].mux_idx = idx; + } else { + if (!is_reachable_nid(codec, spec->adc_nids[c], pin, 0)) + continue; + } + spec->inputs[spec->num_inputs].adc_idx = c; + /* Can primary ADC satisfy all inputs? */ + if (!spec->dyn_adc_switch && + spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { + snd_printd(KERN_INFO + "via: dynamic ADC switching enabled\n"); + spec->dyn_adc_switch = 1; + } + return true; + } + return false; +} + +static int get_mux_nids(struct hda_codec *codec); + +/* parse input-routes; fill ADCs, MUXs and input-src entries */ +static int parse_analog_inputs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; err = via_fill_adcs(codec); if (err < 0) @@ -1906,55 +2077,97 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, err = get_mux_nids(codec); if (err < 0) return err; - cap_nid = spec->mux_nids[0]; - num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (num_idxs <= 0) - return 0; - - /* for internal loopback recording select */ - for (idx = 0; idx < num_idxs; idx++) { - if (pin_idxs[idx] == spec->aa_mix_nid) { - snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); - break; - } + /* fill all input-routes */ + for (i = 0; i < cfg->num_inputs; i++) { + if (add_input_route(codec, cfg->inputs[i].pin)) + spec->inputs[spec->num_inputs++].label = + hda_get_autocfg_input_label(codec, cfg, i); } + /* check for internal loopback recording */ + if (spec->aa_mix_nid && + add_input_route(codec, spec->aa_mix_nid)) + spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; + + return 0; +} + +/* create analog-loopback volume/switch controls */ +static int create_loopback_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; + int i, j, err, idx; + + if (!spec->aa_mix_nid) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { - const char *label; - type = cfg->inputs[i].type; - for (idx = 0; idx < num_idxs; idx++) - if (pin_idxs[idx] == cfg->inputs[i].pin) - break; - if (idx >= num_idxs) - continue; - label = hda_get_autocfg_input_label(codec, cfg, i); + hda_nid_t pin = cfg->inputs[i].pin; + const char *label = hda_get_autocfg_input_label(codec, cfg, i); + if (prev_label && !strcmp(label, prev_label)) type_idx++; else type_idx = 0; prev_label = label; - idx2 = get_connection_index(codec, spec->aa_mix_nid, - pin_idxs[idx]); - if (idx2 >= 0) { + idx = get_connection_index(codec, spec->aa_mix_nid, pin); + if (idx >= 0) { err = via_new_analog_input(spec, label, type_idx, - idx2, spec->aa_mix_nid); + idx, spec->aa_mix_nid); if (err < 0) return err; - add_loopback_list(spec, spec->aa_mix_nid, idx2); + add_loopback_list(spec, spec->aa_mix_nid, idx); } - snd_hda_add_imux_item(imux, label, idx, NULL); /* remember the label for smart51 control */ for (j = 0; j < spec->smart51_nums; j++) { - if (spec->smart51_pins[j] == cfg->inputs[i].pin) { + if (spec->smart51_pins[j] == pin) { spec->smart51_idxs[j] = idx; spec->smart51_labels[j] = label; break; } } } + return 0; +} + +/* create mic-boost controls (if present) */ +static int create_mic_boost_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int caps; + const char *label; + char name[32]; + + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + caps = query_amp_caps(codec, pin, HDA_INPUT); + if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + snprintf(name, sizeof(name), "%s Boost Volume", label); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; +} + +/* create capture and input-src controls for multiple streams */ +static int create_multi_adc_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i, err; /* create capture mixer elements */ for (i = 0; i < spec->num_adc_nids; i++) { @@ -1977,34 +2190,89 @@ static int via_auto_create_analog_input_ctls(struct hda_codec *codec, for (i = 0; i < spec->num_adc_nids; i++) if (!spec->mux_nids[i]) break; - if (i) { - struct snd_kcontrol_new *knew; - knew = via_clone_control(spec, &via_input_src_ctl); - if (!knew) - return -ENOMEM; - knew->count = i; - } + err = create_input_src_ctls(codec, i); + if (err < 0) + return err; + return 0; +} - /* mic-boosts */ - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - unsigned int caps; - const char *label; - char name[32]; +/* bind capture volume/switch */ +static struct snd_kcontrol_new via_bind_cap_vol_ctl = + HDA_BIND_VOL("Capture Volume", 0); +static struct snd_kcontrol_new via_bind_cap_sw_ctl = + HDA_BIND_SW("Capture Switch", 0); - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - caps = query_amp_caps(codec, pin, HDA_INPUT); - if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) - continue; - label = hda_get_autocfg_input_label(codec, cfg, i); - snprintf(name, sizeof(name), "%s Boost Volume", label); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } +static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, + struct hda_ctl_ops *ops) +{ + struct hda_bind_ctls *ctl; + int i; + + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->ops = ops; + for (i = 0; i < spec->num_adc_nids; i++) + ctl->values[i] = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); + *ctl_ret = ctl; + return 0; +} + +/* create capture and input-src controls for dynamic ADC-switch case */ +static int create_dyn_adc_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + int err; + + /* set up the bind capture ctls */ + err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); + if (err < 0) + return err; + err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); + if (err < 0) + return err; + + /* create capture mixer elements */ + knew = via_clone_control(spec, &via_bind_cap_vol_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_vol; + + knew = via_clone_control(spec, &via_bind_cap_sw_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_sw; + + /* input-source control */ + err = create_input_src_ctls(codec, 1); + if (err < 0) + return err; + return 0; +} +/* parse and create capture-related stuff */ +static int via_auto_create_analog_input_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = parse_analog_inputs(codec); + if (err < 0) + return err; + if (spec->dyn_adc_switch) + err = create_dyn_adc_ctls(codec); + else + err = create_multi_adc_ctls(codec); + if (err < 0) + return err; + err = create_loopback_ctls(codec); + if (err < 0) + return err; + err = create_mic_boost_ctls(codec); + if (err < 0) + return err; return 0; } @@ -2090,7 +2358,7 @@ static int via_parse_auto_config(struct hda_codec *codec) err = via_auto_create_speaker_ctls(codec); if (err < 0) return err; - err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_analog_input_ctls(codec); if (err < 0) return err; @@ -2104,8 +2372,6 @@ static int via_parse_auto_config(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; - spec->input_mux = &spec->private_imux[0]; - if (spec->hp_dac_nid && spec->hp_dep_path.depth) { err = via_hp_build(codec); if (err < 0) -- cgit v1.2.3 From 3fccdfd891257acde3351d615ac3cb9c6db71d1f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Jun 2011 10:35:05 +0200 Subject: ALSA: hda - Allow multi-io with HP output for ALC662 & co Even if the machine has no line-out but only HP-out, try to detect the multi-io. It'll allow more possibilities for 5.1 outputs on laptops. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9b97af92e3d6..0f90fac34a76 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -18983,6 +18983,7 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec, hda_nid_t dac; spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.num_dacs = 0; for (i = 0; i < cfg->line_outs; i++) { dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]); if (!dac) @@ -19317,8 +19318,20 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) unsigned int location, defcfg; int num_pins; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) { + /* use HP as primary out */ + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + alc662_auto_fill_dac_nids(codec, cfg); + } if (cfg->line_outs != 1 || - cfg->line_out_type != AUTO_PIN_LINE_OUT) + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) return 0; defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]); @@ -19339,6 +19352,8 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) spec->multi_ios = num_pins; spec->ext_channel_count = 2; spec->multiout.num_dacs = num_pins + 1; + /* for avoiding multi HP mixers */ + cfg->line_out_type = AUTO_PIN_LINE_OUT; } return 0; } -- cgit v1.2.3 From 1af7c5f0d48dca385f29610cc62435afc13237cf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Jun 2011 10:43:03 +0200 Subject: ALSA: hda - Add a workaround for invalid line-out setups Some BIOS set up the pin config wrongly as line-out although it's supposed to be a speaker out. In most cases, though, we can judge the validity by checking the connection type -- when it's FIXED, mostly it's an invalid line-out but a speaker. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a2388fc23e39..654dc8935c91 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4590,7 +4590,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; - short assoc, loc; + short assoc, loc, conn, dev; /* read all default configuration for pin complex */ if (wid_type != AC_WID_PIN) @@ -4600,10 +4600,19 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, continue; def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) continue; loc = get_defcfg_location(def_conf); - switch (get_defcfg_device(def_conf)) { + dev = get_defcfg_device(def_conf); + + /* workaround for buggy BIOS setups */ + if (dev == AC_JACK_LINE_OUT) { + if (conn == AC_JACK_PORT_FIXED) + dev = AC_JACK_SPEAKER; + } + + switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); assoc = get_defcfg_association(def_conf); -- cgit v1.2.3 From 6843ca16f5e381ae80fc563931f8c74bda9fa29a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Jun 2011 11:03:58 +0200 Subject: ALSA: hda - Clean up multi-channel mixer name assignment in patch_realtek.c Change alc_get_line_out_pfx() in patch_realtek.c to provide the channel specific name and assign the index so that each caller doesn't have to set the channel name by itself. Also, check the multi-io case with the primary hp-out; for the multi-io channels, assign the channel name instead of "Headphone" with indices. This makes the mixer names more intuitive and reduces confusion. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 92 +++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b0cf726d4b93..7858da5675fe 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5359,11 +5359,15 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, return 0; } -static const char *alc_get_line_out_pfx(struct alc_spec *spec, - bool can_be_master) +static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, + bool can_be_master, int *index) { struct auto_pin_cfg *cfg = &spec->autocfg; + static const char * const chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; + *index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && !cfg->hp_outs && !cfg->speaker_outs && can_be_master) return "Master"; @@ -5374,23 +5378,23 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, return "Speaker"; break; case AUTO_PIN_HP_OUT: + /* for multi-io case, only the primary out */ + if (ch && spec->multi_ios) + break; + *index = ch; return "Headphone"; default: if (cfg->line_outs == 1 && !spec->multi_ios) return "PCM"; break; } - return NULL; + return chname[ch]; } /* add playback controls from the parsed DAC table */ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, false); hda_nid_t nid; int i, err, noutputs; @@ -5399,10 +5403,13 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, noutputs += spec->multi_ios; for (i = 0; i < noutputs; i++) { + const char *name; + int index; if (!spec->multiout.dac_nids[i]) continue; nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); - if (!pfx && i == 2) { + name = alc_get_line_out_pfx(spec, i, false, &index); + if (!name) { /* Center/LFE */ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Center", @@ -5429,12 +5436,6 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, if (err < 0) return err; } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 0, @@ -12257,17 +12258,18 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, { const char *pfx; int vbits; - int i, err; + int i, index, err; spec->multiout.num_dacs = 1; /* only use one dac */ spec->multiout.dac_nids = spec->private_dac_nids; spec->private_dac_nids[0] = 2; - pfx = alc_get_line_out_pfx(spec, true); - if (!pfx) - pfx = "Front"; for (i = 0; i < 2; i++) { - err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i); + pfx = alc_get_line_out_pfx(spec, i, true, &index); + if (!pfx) + pfx = "PCM"; + err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, + index); if (err < 0) return err; if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { @@ -12287,10 +12289,11 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, vbits = alc262_check_volbit(cfg->line_out_pins[0]) | alc262_check_volbit(cfg->speaker_pins[0]) | alc262_check_volbit(cfg->hp_pins[0]); - if (vbits == 1 || vbits == 2) - pfx = "Master"; /* only one mixer is used */ vbits = 0; for (i = 0; i < 2; i++) { + pfx = alc_get_line_out_pfx(spec, i, true, &index); + if (!pfx) + pfx = "PCM"; err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx, &vbits, i); if (err < 0) @@ -16035,10 +16038,6 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct alc_spec *spec = codec->spec; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, true); hda_nid_t nid; int i, err, noutputs; @@ -16047,10 +16046,13 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, noutputs += spec->multi_ios; for (i = 0; i < noutputs; i++) { + const char *name; + int index; nid = spec->multiout.dac_nids[i]; if (!nid) continue; - if (!pfx && i == 2) { + name = alc_get_line_out_pfx(spec, i, true, &index); + if (!name) { /* Center/LFE */ err = alc861_create_out_sw(codec, "Center", nid, 1); if (err < 0) @@ -16059,12 +16061,6 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, if (err < 0) return err; } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } err = __alc861_create_out_sw(codec, name, nid, index, 3); if (err < 0) return err; @@ -17178,10 +17174,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - static const char * const chname[4] = { - "Front", "Surround", "CLFE", "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, true); hda_nid_t nid_v, nid_s; int i, err, noutputs; @@ -17190,6 +17182,8 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, noutputs += spec->multi_ios; for (i = 0; i < noutputs; i++) { + const char *name; + int index; if (!spec->multiout.dac_nids[i]) continue; nid_v = alc861vd_idx_to_mixer_vol( @@ -17199,7 +17193,8 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, alc880_dac_to_idx( spec->multiout.dac_nids[i])); - if (!pfx && i == 2) { + name = alc_get_line_out_pfx(spec, i, true, &index); + if (!name) { /* Center/LFE */ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Center", @@ -17226,12 +17221,6 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (err < 0) return err; } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, name, index, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, @@ -19030,10 +19019,6 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct alc_spec *spec = codec->spec; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, true); hda_nid_t nid, mix, pin; int i, err, noutputs; @@ -19042,6 +19027,8 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, noutputs += spec->multi_ios; for (i = 0; i < noutputs; i++) { + const char *name; + int index; nid = spec->multiout.dac_nids[i]; if (!nid) continue; @@ -19052,7 +19039,8 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, mix = alc_auto_dac_to_mix(codec, pin, nid); if (!mix) continue; - if (!pfx && i == 2) { + name = alc_get_line_out_pfx(spec, i, true, &index); + if (!name) { /* Center/LFE */ err = alc662_add_vol_ctl(spec, "Center", nid, 1); if (err < 0) @@ -19067,12 +19055,6 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, if (err < 0) return err; } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } err = __alc662_add_vol_ctl(spec, name, nid, index, 3); if (err < 0) return err; @@ -19361,8 +19343,6 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) spec->multi_ios = num_pins; spec->ext_channel_count = 2; spec->multiout.num_dacs = num_pins + 1; - /* for avoiding multi HP mixers */ - cfg->line_out_type = AUTO_PIN_LINE_OUT; } return 0; } -- cgit v1.2.3 From 2e925ddeb90ed13b2908c90c4ec31f17efe84359 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Jun 2011 11:27:22 +0200 Subject: ALSA: hda - Use alc_get_pfx_name() for all Realtek codecs Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7858da5675fe..cb8afdab8349 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7184,12 +7184,8 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->line_out_pins[0]; if (nid) { const char *pfx; - if (!cfg->speaker_pins[0] && !cfg->hp_pins[0]) - pfx = "Master"; - else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - pfx = "Speaker"; - else - pfx = "Front"; + int index; + pfx = alc_get_line_out_pfx(spec, 0, true, &index); err = alc260_add_playback_controls(spec, nid, pfx, &vols); if (err < 0) return err; @@ -13639,10 +13635,8 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, 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"; + int index; + name = alc_get_line_out_pfx(spec, 0, true, &index); err = alc268_new_analog_output(spec, nid, name, 0); if (err < 0) return err; @@ -19871,10 +19865,8 @@ static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec, 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"; + int index; + name = alc_get_line_out_pfx(spec, 0, true, &index); err = alc680_new_analog_output(spec, nid, name, 0); if (err < 0) return err; -- cgit v1.2.3 From dce2079b89b6579c417bad8a7c44de1a89012ffa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Jun 2011 14:10:28 +0200 Subject: ALSA: hda - Add snd_hda_get_conn_list() helper function Add a new helper function snd_hda_get_conn_list(). Unlike snd_hda_get_connections(), this function doesn't copy the connection-list but gives the raw pointer for the cached list. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 69 ++++++++++++++++++++++++++++++----------------- sound/pci/hda/hda_codec.h | 2 ++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 654dc8935c91..26c420de91c3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -311,35 +311,35 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); static bool add_conn_list(struct snd_array *array, hda_nid_t nid); -static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst, - hda_nid_t *src, int len); /** * snd_hda_get_connections - get connection list * @codec: the HDA codec * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store + * @listp: the pointer to store NID list * * Parses the connection list of the given widget and stores the list * of NIDs. * * Returns the number of connections, or a negative error code. */ -int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp) { struct snd_array *array = &codec->conn_lists; int i, len, old_used; hda_nid_t list[HDA_MAX_CONNECTIONS]; + hda_nid_t *p; /* look up the cached results */ for (i = 0; i < array->used; ) { - hda_nid_t *p = snd_array_elem(array, i); + p = snd_array_elem(array, i); len = p[1]; - if (nid == *p) - return copy_conn_list(nid, conn_list, max_conns, - p + 2, len); + if (nid == *p) { + if (listp) + *listp = p + 2; + return len; + } i += len + 2; } @@ -355,12 +355,46 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, if (!add_conn_list(array, list[i])) goto error_add; - return copy_conn_list(nid, conn_list, max_conns, list, len); + p = snd_array_elem(array, old_used); + if (listp) + *listp = p + 2; + return len; error_add: array->used = old_used; return -ENOMEM; } +EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); + +/** + * snd_hda_get_connections - copy connection list + * @codec: the HDA codec + * @nid: NID to parse + * @conn_list: connection list array + * @max_conns: max. number of connections to store + * + * Parses the connection list of the given widget and stores the list + * of NIDs. + * + * Returns the number of connections, or a negative error code. + */ +int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) +{ + const hda_nid_t *list; + int len = snd_hda_get_conn_list(codec, nid, &list); + + if (len <= 0) + return len; + if (len > max_conns) { + snd_printk(KERN_ERR "hda_codec: " + "Too many connections %d for NID 0x%x\n", + len, nid); + return -EINVAL; + } + memcpy(conn_list, list, len * sizeof(hda_nid_t)); + return len; +} EXPORT_SYMBOL_HDA(snd_hda_get_connections); static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, @@ -471,19 +505,6 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) return true; } -static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst, - hda_nid_t *src, int len) -{ - if (len > max_dst) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - len, nid); - return -EINVAL; - } - memcpy(dst, src, len * sizeof(hda_nid_t)); - return len; -} - /** * snd_hda_queue_unsol_event - add an unsolicited event to queue * @bus: the BUS diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 070efac7e207..c71cd7fb6d11 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -903,6 +903,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); -- cgit v1.2.3 From 1f0f4b8036b1fe1347cb4f1f199601b87de9be46 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 10:52:59 +0200 Subject: ALSA: hda - Reduce static init verbs for Realtek auto-parsers Instead of using fixed init verbs, initialize DACs, ADCs and mixers more dynamically for Realtek auto-parsers. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 501 ++++++++++++------------------------------ 1 file changed, 140 insertions(+), 361 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cb8afdab8349..480c4233cca5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -348,6 +348,7 @@ struct alc_spec { const hda_nid_t *adc_nids; const hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + hda_nid_t mixer_nid; /* analog-mixer NID */ /* capture setup for dynamic dual-adc switch */ unsigned int cur_adc_idx; @@ -2061,15 +2062,23 @@ static void alc_auto_init_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i; - hda_nid_t pin; + hda_nid_t pin, dac; for (i = 0; i < spec->autocfg.dig_outs; i++) { pin = spec->autocfg.dig_out_pins[i]; - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - } + if (!pin) + continue; + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (!i) + dac = spec->multiout.dig_out_nid; + else + dac = spec->slave_dig_outs[i - 1]; + if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) + continue; + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); } pin = spec->autocfg.dig_in_pin; if (pin) @@ -5602,6 +5611,19 @@ static int get_pin_type(int line_out_type) return PIN_OUT; } +static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return; + nid = alc880_idx_to_mixer(alc880_dac_to_idx(nid)); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(1)); +} + static void alc880_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5612,12 +5634,16 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec) int pin_type = get_pin_type(spec->autocfg.line_out_type); alc880_auto_set_output_and_unmute(codec, nid, pin_type, i); } + /* mute DACs */ + for (i = 0; i < spec->multiout.num_dacs; i++) + alc880_auto_init_dac(codec, spec->multiout.dac_nids[i]); } static void alc880_auto_init_extra_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t pin; + int i; pin = spec->autocfg.speaker_pins[0]; if (pin) /* connect to front */ @@ -5625,6 +5651,10 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec) pin = spec->autocfg.hp_pins[0]; if (pin) /* connect to front */ alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + /* mute DACs */ + alc880_auto_init_dac(codec, spec->multiout.hp_nid); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) + alc880_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); } static void alc880_auto_init_analog_input(struct hda_codec *codec) @@ -5637,13 +5667,21 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec) hda_nid_t nid = cfg->inputs[i].pin; if (alc_is_input_pin(codec, nid)) { alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC880_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); } } + + /* mute all loopback inputs */ + if (spec->mixer_nid) { + int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL); + for (i = 0; i < nums; i++) + snd_hda_codec_write(codec, spec->mixer_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } } static void alc880_auto_init_input_src(struct hda_codec *codec) @@ -5662,6 +5700,9 @@ static void alc880_auto_init_input_src(struct hda_codec *codec) snd_hda_codec_write(codec, spec->adc_nids[c], 0, AC_VERB_SET_CONNECT_SEL, imux->items[0].index); + snd_hda_codec_write(codec, spec->adc_nids[c], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); } } @@ -5713,8 +5754,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc880_volume_init_verbs); - spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -5984,6 +6023,8 @@ static int patch_alc880(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST, alc880_models, alc880_cfg_tbl); @@ -7228,10 +7269,24 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, } } +static void alc260_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return; + nid += 0x06; /* DAC -> MIX */ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(1)); +} + static void alc260_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t nid; + int i; nid = spec->autocfg.line_out_pins[0]; if (nid) { @@ -7246,74 +7301,17 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) nid = spec->autocfg.hp_pins[0]; if (nid) alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); -} - -#define ALC260_PIN_CD_NID 0x16 -static void alc260_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (nid >= 0x12) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC260_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } + /* mute DACs */ + for (i = 0; i < spec->multiout.num_dacs; i++) + alc260_auto_init_dac(codec, spec->multiout.dac_nids[i]); + alc260_auto_init_dac(codec, spec->multiout.extra_out_nid[0]); + alc260_auto_init_dac(codec, spec->multiout.hp_nid); } +#define alc260_auto_init_analog_input alc880_auto_init_analog_input #define alc260_auto_init_input_src alc880_auto_init_input_src -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc260_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, 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 */ - /* mute analog inputs */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* - * Set up output mixers (0x08 - 0x0a) - */ - /* set vol=0 to output mixers */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - { } -}; - static int alc260_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -7340,8 +7338,6 @@ static int alc260_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc260_volume_init_verbs); - spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -7588,6 +7584,8 @@ static int patch_alc260(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x07; + board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST, alc260_models, alc260_cfg_tbl); @@ -9035,48 +9033,6 @@ static void alc885_imac24_init_hook(struct hda_codec *codec) alc_hp_automute(codec); } -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc883_auto_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* - * Set up output mixers (0x0c - 0x0f) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, 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)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - /* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */ static const struct hda_verb alc889A_mb31_ch2_init[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ @@ -11036,6 +10992,9 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, /* set as output */ alc_set_pin_output(codec, nid, pin_type); + if (snd_hda_get_conn_list(codec, nid, NULL) < 2) + return; + if (dac == 0x25) idx = 4; else if (dac >= 0x02 && dac <= 0x05) @@ -11045,6 +11004,8 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } +#define alc882_auto_init_dac alc880_auto_init_dac + static void alc882_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -11057,6 +11018,9 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec) alc882_auto_set_output_and_unmute(codec, nid, pin_type, spec->multiout.dac_nids[i]); } + /* mute DACs */ + for (i = 0; i < spec->multiout.num_dacs; i++) + alc882_auto_init_dac(codec, spec->multiout.dac_nids[i]); } static void alc882_auto_init_hp_out(struct hda_codec *codec) @@ -11088,31 +11052,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); } } -} - -static void alc882_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } + /* mute DACs */ + alc882_auto_init_dac(codec, spec->multiout.hp_nid); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) + alc882_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); } +#define alc882_auto_init_analog_input alc880_auto_init_analog_input + static void alc882_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int c; for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t conn_list[HDA_MAX_NUM_INPUTS]; hda_nid_t nid = spec->capsrc_nids[c]; unsigned int mux_idx; const struct hda_input_mux *imux; @@ -11123,9 +11077,8 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); - conns = snd_hda_get_connections(codec, nid, conn_list, - ARRAY_SIZE(conn_list)); - if (conns < 0) + conns = snd_hda_get_conn_list(codec, nid, NULL); + if (conns <= 0) continue; mux_idx = c >= spec->num_mux_defs ? 0 : c; imux = &spec->input_mux[mux_idx]; @@ -11241,7 +11194,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc883_auto_init_verbs); /* if ADC 0x07 is available, initialize it, too */ if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN) add_verb(spec, alc882_adc1_init_verbs); @@ -11282,6 +11234,8 @@ static int patch_alc882(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + switch (codec->vendor_id) { case 0x10ec0882: case 0x10ec0885: @@ -11353,7 +11307,6 @@ static int patch_alc882(struct hda_codec *codec) for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) { const struct hda_input_mux *imux = spec->input_mux; hda_nid_t cap; - hda_nid_t items[16]; hda_nid_t nid = alc882_adc_nids[i]; unsigned int wcap = get_wcaps(codec, nid); /* get type */ @@ -11364,8 +11317,7 @@ static int patch_alc882(struct hda_codec *codec) err = snd_hda_get_connections(codec, nid, &cap, 1); if (err < 0) continue; - err = snd_hda_get_connections(codec, cap, items, - ARRAY_SIZE(items)); + err = snd_hda_get_conn_list(codec, cap, NULL); if (err < 0) continue; for (j = 0; j < imux->num_items; j++) @@ -12313,70 +12265,6 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, #define alc262_auto_create_input_ctls \ alc882_auto_create_input_ctls -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc262_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (CD, 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 - 0x0f) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, 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)}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - - { } -}; - static const struct hda_verb alc262_HP_BPC_init_verbs[] = { /* * Unmute ADC0-2 and set the default input to mic-in @@ -12674,7 +12562,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc262_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -13034,6 +12921,9 @@ static int patch_alc262(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + + spec->mixer_nid = 0x0b; + #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is * under-run @@ -13450,6 +13340,8 @@ static const struct hda_verb alc268_base_init_verbs[] = { { } }; +/* only for model=test */ +#ifdef CONFIG_SND_DEBUG /* * generic initialization of ADC, input mixers and output mixers */ @@ -13470,12 +13362,15 @@ static const struct hda_verb alc268_volume_init_verbs[] = { {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; +#endif /* CONFIG_SND_DEBUG */ - /* set PCBEEP vol = 0, mute connections */ +/* set PCBEEP vol = 0, mute connections */ +static const struct hda_verb alc268_beep_init_verbs[] = { {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } }; @@ -13690,6 +13585,14 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } +static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +} + static void alc268_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -13700,6 +13603,9 @@ static void alc268_auto_init_multi_out(struct hda_codec *codec) int pin_type = get_pin_type(spec->autocfg.line_out_type); alc268_auto_set_output_and_unmute(codec, nid, pin_type); } + /* mute DACs */ + for (i = 0; i < spec->multiout.num_dacs; i++) + alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]); } static void alc268_auto_init_hp_out(struct hda_codec *codec) @@ -13719,6 +13625,10 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec) if (spec->autocfg.mono_out_pin) snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* mute DACs */ + alc268_auto_init_dac(codec, spec->multiout.hp_nid); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) + alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); } static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) @@ -13812,7 +13722,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) add_mixer(spec, alc268_beep_mixer); - add_verb(spec, alc268_volume_init_verbs); + add_verb(spec, alc268_beep_init_verbs); spec->num_mux_defs = 2; spec->input_mux = &spec->private_imux[0]; @@ -14037,7 +13947,8 @@ static const struct alc_config_preset alc268_presets[] = { [ALC268_TEST] = { .mixers = { alc268_test_mixer, alc268_capture_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_volume_init_verbs }, + alc268_volume_init_verbs, + alc268_beep_init_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), @@ -14064,6 +13975,8 @@ static int patch_alc268(struct hda_codec *codec) codec->spec = spec; + /* ALC268 has no aa-loopback mixer */ + board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST, alc268_models, alc268_cfg_tbl); @@ -14775,13 +14688,10 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - if (spec->codec_variant != ALC269_TYPE_NORMAL) { - add_verb(spec, alc269vb_init_verbs); + if (spec->codec_variant != ALC269_TYPE_NORMAL) alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); - } else { - add_verb(spec, alc269_init_verbs); + else alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - } spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -15231,6 +15141,8 @@ static int patch_alc269(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + alc_auto_parse_customize_define(codec); if (codec->vendor_id == 0x10ec0269) { @@ -15858,58 +15770,6 @@ static const struct hda_verb alc861_asus_laptop_init_verbs[] = { { } }; -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc861_auto_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute DAC0~3 & spdif out*/ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */ - - { } -}; - static const struct hda_verb alc861_toshiba_init_verbs[] = { {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, @@ -16123,7 +15983,7 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - for (i = 0; i < spec->autocfg.line_outs; i++) { + for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); if (nid) @@ -16148,18 +16008,7 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) spec->multiout.dac_nids[0]); } -static void alc861_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (nid >= 0x0c && nid <= 0x11) - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - } -} +#define alc861_auto_init_analog_input alc880_auto_init_analog_input /* parse the BIOS configuration and set up the alc_spec */ /* return 1 if successful, 0 if the proper config is not found, @@ -16201,8 +16050,6 @@ static int alc861_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc861_auto_init_verbs); - spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -16417,6 +16264,8 @@ static int patch_alc861(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x15; + board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST, alc861_models, alc861_cfg_tbl); @@ -17102,61 +16951,9 @@ static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, } -static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, int dac_idx) -{ - alc_set_pin_output(codec, nid, pin_type); -} - -static void alc861vd_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc861vd_auto_set_output_and_unmute(codec, nid, - pin_type, i); - } -} - - -static void alc861vd_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) /* connect to front and use dac 0 */ - alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); -} - -#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID - -static void alc861vd_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC861VD_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } -} - +#define alc861vd_auto_init_multi_out alc882_auto_init_multi_out +#define alc861vd_auto_init_hp_out alc882_auto_init_hp_out +#define alc861vd_auto_init_analog_input alc882_auto_init_analog_input #define alc861vd_auto_init_input_src alc882_auto_init_input_src #define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) @@ -17324,8 +17121,6 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc861vd_volume_init_verbs); - spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -17384,6 +17179,8 @@ static int patch_alc861vd(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST, alc861vd_models, alc861vd_cfg_tbl); @@ -19153,27 +18950,7 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) spec->multiout.extra_out_nid[0]); } -#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID - -static void alc662_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC662_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } -} - +#define alc662_auto_init_analog_input alc882_auto_init_analog_input #define alc662_auto_init_input_src alc882_auto_init_input_src /* @@ -19499,6 +19276,8 @@ static int patch_alc662(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + alc_auto_parse_customize_define(codec); alc_fix_pll_init(codec, 0x20, 0x04, 15); @@ -19958,8 +19737,6 @@ static int alc680_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc680_init_verbs); - err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -20023,6 +19800,8 @@ static int patch_alc680(struct hda_codec *codec) codec->spec = spec; + /* ALC680 has no aa-loopback mixer */ + board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST, alc680_models, alc680_cfg_tbl); -- cgit v1.2.3 From cb053a8265954518d4c9e865d8a0d682405825d2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 11:32:07 +0200 Subject: ALSA: hda - Call proper DAC-filler function for Realtek auto-parser In alc_auto_add_multi_channel_mode(), when the primary HP workaround is enabled, it re-initializes the DAC list but calls alc662's function in a fixed way. This isn't pretty suitable for other codecs, of course. Now we call it with fill_dac function pointer so that the proper function can be called at that point. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 480c4233cca5..14058a4cb6ca 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5330,9 +5330,10 @@ static int add_control_with_pfx(struct alc_spec *spec, int type, #define ALC880_PIN_CD_NID 0x1c /* fill in the dac_nids table from the parsed pin configuration */ -static int alc880_auto_fill_dac_nids(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) +static int alc880_auto_fill_dac_nids(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; int assigned[4]; int i, j; @@ -5706,7 +5707,8 @@ static void alc880_auto_init_input_src(struct hda_codec *codec) } } -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec); +static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, + int (*fill_dac)(struct hda_codec *)); /* parse the BIOS configuration and set up the alc_spec */ /* return 1 if successful, 0 if the proper config is not found, @@ -5725,10 +5727,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); + err = alc880_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec); + err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids); if (err < 0) return err; err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); @@ -11165,10 +11167,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); + err = alc880_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec); + err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids); if (err < 0) return err; err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); @@ -15859,10 +15861,10 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) } /* fill in the dac_nids table from the parsed pin configuration */ -static int alc861_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) +static int alc861_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; int i; hda_nid_t nid, dac; @@ -16027,10 +16029,10 @@ static int alc861_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc861_auto_fill_dac_nids(codec, &spec->autocfg); + err = alc861_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec); + err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids); if (err < 0) return err; err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg); @@ -17091,10 +17093,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); + err = alc880_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec); + err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids); if (err < 0) return err; err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); @@ -18764,10 +18766,10 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) } /* fill in the dac_nids table from the parsed pin configuration */ -static int alc662_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) +static int alc662_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; int i; hda_nid_t dac; @@ -19073,7 +19075,8 @@ static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { .put = alc_auto_ch_mode_put, }; -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) +static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, + int (*fill_dac)(struct hda_codec *)) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; @@ -19090,7 +19093,8 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) cfg->hp_outs = 0; memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); cfg->line_out_type = AUTO_PIN_HP_OUT; - alc662_auto_fill_dac_nids(codec, cfg); + if (fill_dac) + fill_dac(codec); } if (cfg->line_outs != 1 || cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -19131,10 +19135,10 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc662_auto_fill_dac_nids(codec, &spec->autocfg); + err = alc662_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec); + err = alc_auto_add_multi_channel_mode(codec, alc662_auto_fill_dac_nids); if (err < 0) return err; err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg); -- cgit v1.2.3 From 3af9ee6b83c4c3f2577719e31e7d2af1ce996557 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 12:34:01 +0200 Subject: ALSA: hda - Check hard-wired DACs at first for ALC662 & co Some Realtek codecs have the output pins hardwired with certain DACs. These DACs have to be assigned at first and assign the rest for multi-DAC pins so that all DACs can be assigned properly. Without such an optimization, speaker outputs may be assigned to the same DAC as the headphone or others. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 112 ++++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 31 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 14058a4cb6ca..5e4efb75879e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1760,6 +1760,15 @@ do_sku: return 0; } +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return true; + return false; +} + /* check subsystem ID and set up device-specific initialization; * return 1 if initialized, 0 if invalid SSID */ @@ -1869,9 +1878,9 @@ do_sku: nid = porti; else return 1; - for (i = 0; i < spec->autocfg.line_outs; i++) - if (spec->autocfg.line_out_pins[i] == nid) - return 1; + if (found_in_nid_list(nid, spec->autocfg.line_out_pins, + spec->autocfg.line_outs)) + return 1; spec->autocfg.hp_pins[0] = nid; } return 1; @@ -15839,7 +15848,7 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; hda_nid_t mix, srcs[5]; - int i, j, num; + int i, num; if (snd_hda_get_connections(codec, pin, &mix, 1) != 1) return 0; @@ -15851,10 +15860,8 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) type = get_wcaps_type(get_wcaps(codec, srcs[i])); if (type != AC_WID_AUD_OUT) continue; - for (j = 0; j < spec->multiout.num_dacs; j++) - if (spec->multiout.dac_nids[j] == srcs[i]) - break; - if (j >= spec->multiout.num_dacs) + if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids, + spec->multiout.num_dacs)) return srcs[i]; } return 0; @@ -18748,7 +18755,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; hda_nid_t srcs[5]; - int i, j, num; + int i, num; pin = alc_go_down_to_selector(codec, pin); num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); @@ -18756,31 +18763,78 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); if (!nid) continue; - for (j = 0; j < spec->multiout.num_dacs; j++) - if (spec->multiout.dac_nids[j] == nid) - break; - if (j >= spec->multiout.num_dacs) - return nid; + if (found_in_nid_list(nid, spec->multiout.dac_nids, + spec->multiout.num_dacs)) + continue; + if (spec->multiout.hp_nid == nid) + continue; + if (found_in_nid_list(nid, spec->multiout.extra_out_nid, + ARRAY_SIZE(spec->multiout.extra_out_nid))) + continue; + return nid; } return 0; } +static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t sel = alc_go_down_to_selector(codec, pin); + if (snd_hda_get_conn_list(codec, sel, NULL) == 1) + return alc_auto_look_for_dac(codec, pin); + return 0; +} + /* fill in the dac_nids table from the parsed pin configuration */ static int alc662_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; + bool redone; int i; - hda_nid_t dac; - spec->multiout.dac_nids = spec->private_dac_nids; + again: spec->multiout.num_dacs = 0; + spec->multiout.hp_nid = 0; + spec->multiout.extra_out_nid[0] = 0; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + spec->multiout.dac_nids = spec->private_dac_nids; + + /* fill hard-wired DACs first */ + if (!redone) { + for (i = 0; i < cfg->line_outs; i++) + spec->private_dac_nids[i] = + get_dac_if_single(codec, cfg->line_out_pins[i]); + if (cfg->hp_outs) + spec->multiout.hp_nid = + get_dac_if_single(codec, cfg->hp_pins[0]); + if (cfg->speaker_outs) + spec->multiout.extra_out_nid[0] = + get_dac_if_single(codec, cfg->speaker_pins[0]); + } + for (i = 0; i < cfg->line_outs; i++) { - dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]); - if (!dac) + hda_nid_t pin = cfg->line_out_pins[i]; + if (spec->private_dac_nids[i]) continue; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin); + if (!spec->private_dac_nids[i] && !redone) { + /* if we can't find primary DACs, re-probe without + * checking the hard-wired DACs + */ + redone = true; + goto again; + } + } + + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); } + return 0; } @@ -18860,18 +18914,16 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, } /* add playback controls for speaker and HP outputs */ -/* return DAC nid if any new DAC is assigned */ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - const char *pfx) + hda_nid_t dac, const char *pfx) { struct alc_spec *spec = codec->spec; - hda_nid_t nid, mix; + hda_nid_t mix; int err; if (!pin) return 0; - nid = alc_auto_look_for_dac(codec, pin); - if (!nid) { + if (!dac) { /* the corresponding DAC is already occupied */ if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) return 0; /* no way */ @@ -18880,16 +18932,16 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } - mix = alc_auto_dac_to_mix(codec, pin, nid); + mix = alc_auto_dac_to_mix(codec, pin, dac); if (!mix) return 0; - err = alc662_add_vol_ctl(spec, pfx, nid, 3); + err = alc662_add_vol_ctl(spec, pfx, dac, 3); if (err < 0) return err; err = alc662_add_sw_ctl(spec, pfx, mix, 3); if (err < 0) return err; - return nid; + return 0; } /* create playback/capture controls for input pins */ @@ -19146,17 +19198,15 @@ static int alc662_parse_auto_config(struct hda_codec *codec) return err; err = alc662_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], + spec->multiout.extra_out_nid[0], "Speaker"); if (err < 0) return err; - if (err) - spec->multiout.extra_out_nid[0] = err; err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + spec->multiout.hp_nid, "Headphone"); if (err < 0) return err; - if (err) - spec->multiout.hp_nid = err; err = alc662_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; -- cgit v1.2.3 From 6d86b4fb407995081c85106188e2d2404529d71c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 15:00:48 +0200 Subject: ALSA: hda - Fix auto-init of output volumes of Realtek codecs Fix the regression introduced by the commit 1f0f4b8036b1fe1347cb4f1f199601b87de9be46 ALSA: hda - Reduce static init verbs for Realtek auto-parsers The input amps of mixer widgets should be unmuted as default (as usually they have no assigned mixer switches). More fixes in this commit are, however, for ALC260: ALC260 codec can have multiple output mixers connnected to a single DAC althouh the driver didn't pick up them properly. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 64 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5e4efb75879e..b2dcb84dcbb6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5629,9 +5629,9 @@ static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); + AMP_IN_UNMUTE(0)); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(1)); + AMP_IN_UNMUTE(1)); } static void alc880_auto_init_multi_out(struct hda_codec *codec) @@ -7186,27 +7186,33 @@ static const struct hda_verb alc260_test_init_verbs[] = { * for BIOS auto-configuration */ +/* convert from pin to volume-mixer widget */ +static hda_nid_t alc260_pin_to_vol_mix(hda_nid_t nid) +{ + if (nid >= 0x0f && nid <= 0x11) + return nid - 0x7; + else if (nid >= 0x12 && nid <= 0x15) + return 0x08; + else + return 0; +} + static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, const char *pfx, int *vol_bits) { hda_nid_t nid_vol; unsigned long vol_val, sw_val; - int err; + int chs, err; - if (nid >= 0x0f && nid < 0x11) { - nid_vol = nid - 0x7; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - } else if (nid == 0x11) { - nid_vol = nid - 0x7; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); - } else if (nid >= 0x12 && nid <= 0x15) { - nid_vol = 0x08; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - } else + nid_vol = alc260_pin_to_vol_mix(nid); + if (!nid_vol) return 0; /* N/A */ + if (nid == 0x11) + chs = 2; + else + chs = 3; + vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, chs, 0, HDA_OUTPUT); + sw_val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); if (!(*vol_bits & (1 << nid_vol))) { /* first control for the volume widget */ @@ -7271,6 +7277,8 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int sel_idx) { + hda_nid_t mix; + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (nid >= 0x12) { @@ -7278,26 +7286,22 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, snd_hda_codec_write(codec, idx + 0x0b, 0, AC_VERB_SET_CONNECT_SEL, sel_idx); } -} -static void alc260_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) -{ - if (!nid) + mix = alc260_pin_to_vol_mix(nid); + if (!mix) return; - nid += 0x06; /* DAC -> MIX */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(1)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); } static void alc260_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t nid; - int i; nid = spec->autocfg.line_out_pins[0]; if (nid) { @@ -7312,12 +7316,6 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) nid = spec->autocfg.hp_pins[0]; if (nid) alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); - - /* mute DACs */ - for (i = 0; i < spec->multiout.num_dacs; i++) - alc260_auto_init_dac(codec, spec->multiout.dac_nids[i]); - alc260_auto_init_dac(codec, spec->multiout.extra_out_nid[0]); - alc260_auto_init_dac(codec, spec->multiout.hp_nid); } #define alc260_auto_init_analog_input alc880_auto_init_analog_input -- cgit v1.2.3 From 39fa84e94a7df64a6ba27669ef98b51994fb6894 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 15:28:57 +0200 Subject: ALSA: hda - Simplify EAPD control in patch_realtek.c Look through the known NIDs that may have EAPD capabilities and turn on/off them appropriately instead of checking the individual vendor ids. This will also avoid the forgotten entries of newly added codec ids in future. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b2dcb84dcbb6..783017d9247f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1366,28 +1366,12 @@ static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) { /* We currently only handle front, HP */ - switch (codec->vendor_id) { - case 0x10ec0260: - set_eapd(codec, 0x0f, on); - set_eapd(codec, 0x10, on); - break; - case 0x10ec0262: - case 0x10ec0267: - case 0x10ec0268: - case 0x10ec0269: - case 0x10ec0270: - case 0x10ec0272: - case 0x10ec0660: - case 0x10ec0662: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0862: - case 0x10ec0889: - case 0x10ec0892: - set_eapd(codec, 0x14, on); - set_eapd(codec, 0x15, on); - break; - } + static hda_nid_t pins[] = { + 0x0f, 0x10, 0x14, 0x15, 0 + }; + hda_nid_t *p; + for (p = pins; *p; p++) + set_eapd(codec, *p, on); } /* generic shutup callback; @@ -1403,6 +1387,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) { unsigned int tmp; + alc_auto_setup_eapd(codec, true); switch (type) { case ALC_INIT_GPIO1: snd_hda_sequence_write(codec, alc_gpio1_init_verbs); @@ -1414,7 +1399,6 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; case ALC_INIT_DEFAULT: - alc_auto_setup_eapd(codec, true); switch (codec->vendor_id) { case 0x10ec0260: snd_hda_codec_write(codec, 0x1a, 0, -- cgit v1.2.3 From 050ea75317f33e376cc413359c0319ad5cc86e2a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 15:48:17 +0200 Subject: ALSA: hda - Fix volume-init of ALC299 & co ALC269 and compatible codecs have the output volume in DACs, thus we can't use the ALC880's code as is. Fixed by checking the amp caps and picking up the right widget for initialization. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 783017d9247f..5293f7f7f425 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5605,17 +5605,28 @@ static int get_pin_type(int line_out_type) return PIN_OUT; } -static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) +static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t dac) { - if (!nid) + hda_nid_t nid, mix; + + if (!dac) return; - nid = alc880_idx_to_mixer(alc880_dac_to_idx(nid)); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); + mix = alc880_idx_to_mixer(alc880_dac_to_idx(dac)); + if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = dac; + else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = mix; + else + nid = 0; + if (nid) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + if (query_amp_caps(codec, mix, HDA_INPUT) & AC_AMPCAP_MUTE) { + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + } } static void alc880_auto_init_multi_out(struct hda_codec *codec) -- cgit v1.2.3 From 7ec9c6ccc6007b14a916021d4ba7ffbcc7822ae3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 15:53:38 +0200 Subject: ALSA: hda - Fix volume-init for ALC259 with invalid widget caps ALC259 seems to provide an invalid widget capability for the input-src selector widget. The widget shows the input-amp while it's a selector, and this confuses the current ALC882 initialization code that is used for ALC259, too. For fixing this, check the amp capability and handle the connection selection individually. Also, ALC259 has no mute bit in DAC volume, so we need to initialize it as ZERO instead of MUTE. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5293f7f7f425..0fefc656c6e0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11075,6 +11075,7 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) unsigned int mux_idx; const struct hda_input_mux *imux; int conns, mute, idx, item; + unsigned int wid_type; /* mute ADC */ snd_hda_codec_write(codec, spec->adc_nids[c], 0, @@ -11088,6 +11089,7 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) imux = &spec->input_mux[mux_idx]; if (!imux->num_items && mux_idx > 0) imux = &spec->input_mux[0]; + wid_type = get_wcaps_type(get_wcaps(codec, nid)); for (idx = 0; idx < conns; idx++) { /* if the current connection is the selected one, * unmute it as default - otherwise mute it @@ -11100,17 +11102,13 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) break; } } - /* check if we have a selector or mixer - * we could check for the widget type instead, but - * just check for Amp-In presence (in case of mixer - * without amp-in there is something wrong, this - * function shouldn't be used or capsrc nid is wrong) - */ - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) + /* initialize the mute status if mute-amp is present */ + if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, mute); - else if (mute != AMP_IN_MUTE(idx)) + if (wid_type == AC_WID_AUD_SEL && + mute != AMP_IN_MUTE(idx)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); @@ -13594,7 +13592,7 @@ static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) if (!nid) return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); + AMP_OUT_ZERO); } static void alc268_auto_init_multi_out(struct hda_codec *codec) -- cgit v1.2.3 From 4f574b7b1a1cc8aac617e938459e8f03a641e678 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Jun 2011 16:17:07 +0200 Subject: ALSA: hda - More volume-init fixes for ALC267 codec More similar fixes like previous commits: handle the exceptional case like ALC267 where no volume amp is found in ADC widget but in the capsrc widget instead. Also minor checks for avoiding possible erros: no connection-select when the pin has a single selection, and add beep verbs only when the 0x1d is used for beep. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0fefc656c6e0..cf383ede281d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11078,9 +11078,16 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) unsigned int wid_type; /* mute ADC */ - snd_hda_codec_write(codec, spec->adc_nids[c], 0, + if (query_amp_caps(codec, spec->adc_nids[c], HDA_INPUT) & + AC_AMPCAP_MUTE) + snd_hda_codec_write(codec, spec->adc_nids[c], 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); + else if (query_amp_caps(codec, nid, HDA_OUTPUT) & + AC_AMPCAP_MUTE) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); conns = snd_hda_get_conn_list(codec, nid, NULL); if (conns <= 0) @@ -13580,6 +13587,8 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, int idx; alc_set_pin_output(codec, nid, pin_type); + if (snd_hda_get_conn_list(codec, nid, NULL) <= 1) + return; if (nid == 0x14 || nid == 0x16) idx = 0; else @@ -13721,10 +13730,11 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) + if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); + add_verb(spec, alc268_beep_init_verbs); + } - add_verb(spec, alc268_beep_init_verbs); spec->num_mux_defs = 2; spec->input_mux = &spec->private_imux[0]; -- cgit v1.2.3 From 880a050f4ac4399c9da4aea3ca19a701f9a727e0 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 28 Jun 2011 16:50:39 +1000 Subject: ALSA: hda - remove SND_HDA_POWER_SAVE protection of struct hda_loopback_check to fix build problems when it is disabled. Signed-off-by: Stephen Rothwell Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_local.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index b333bf46a19c..88b277e97409 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -565,7 +565,6 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) * power-management */ -#ifdef CONFIG_SND_HDA_POWER_SAVE void snd_hda_schedule_power_save(struct hda_codec *codec); struct hda_amp_list { @@ -582,7 +581,6 @@ struct hda_loopback_check { int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid); -#endif /* CONFIG_SND_HDA_POWER_SAVE */ /* * AMP control callbacks -- cgit v1.2.3 From ff2b7e2a3fdb341a235e0b2eccc3c8914d26c1fb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Jun 2011 08:59:30 +0200 Subject: ALSA: hda - Fix warnings with CONFIG_SND_POWER_SAVE=n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use static inline for dummy function to fix the warnings like below sound/pci/hda/patch_sigmatel.c: In function ‘stac92xx_init’: sound/pci/hda/patch_sigmatel.c:4387:3: warning: statement with no effect sound/pci/hda/patch_sigmatel.c: In function ‘stac92xx_resume’: sound/pci/hda/patch_sigmatel.c:4927:3: warning: statement with no effect Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c71cd7fb6d11..79ef65e3ae14 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -1011,17 +1011,15 @@ int snd_hda_suspend(struct hda_bus *bus); int snd_hda_resume(struct hda_bus *bus); #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE static inline int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) { +#ifdef CONFIG_SND_HDA_POWER_SAVE if (codec->patch_ops.check_power_status) return codec->patch_ops.check_power_status(codec, nid); +#endif return 0; } -#else -#define hda_call_check_power_status(codec, nid) 0 -#endif /* * get widget information -- cgit v1.2.3 From 63f10d2ca78c17cdd612c1daee7daffacca8b7fb Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 28 Jun 2011 17:29:10 +0800 Subject: ALSA: hda - Fix unsol event initializations for VIA codecs Fix a issue to enable unsolicited response to line-out pins. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fb5468b4c55a..997b7057a549 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2427,7 +2427,7 @@ static void via_auto_init_unsol_event(struct hda_codec *codec) for (i = 0; i < cfg->line_outs; i++) { if (cfg->line_out_pins[i] && is_jack_detectable(codec, cfg->line_out_pins[i])) - snd_hda_codec_write(codec, cfg->line_out_pins[0], 0, + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ev | VIA_JACK_EVENT); } -- cgit v1.2.3 From 8d087c7600499463b7b8e3d4da4da40669cb8bfa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Jun 2011 12:45:47 +0200 Subject: ALSA: hda - Create snd_hda_get_conn_index() helper function Create snd_hda_get_conn_index() helper function for obtaining the connection index of the widget. Replaced the similar codes used in several codec-drivers with this common helper. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 42 +++++++++++++++++++++++++++++++++++++----- sound/pci/hda/hda_codec.h | 2 ++ sound/pci/hda/patch_cirrus.c | 16 +++++----------- sound/pci/hda/patch_cmedia.c | 11 ++++------- sound/pci/hda/patch_conexant.c | 15 ++------------- sound/pci/hda/patch_realtek.c | 14 ++------------ sound/pci/hda/patch_sigmatel.c | 27 +++------------------------ sound/pci/hda/patch_via.c | 40 ++++------------------------------------ 8 files changed, 59 insertions(+), 108 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 26c420de91c3..7f8502388a82 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -411,11 +411,8 @@ static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, wcaps = get_wcaps(codec, nid); if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) { - snd_printk(KERN_WARNING "hda_codec: " - "connection list not available for 0x%x\n", nid); - return -EINVAL; - } + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); if (parm & AC_CLIST_LONG) { @@ -505,6 +502,41 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) return true; } +/** + * snd_hda_get_conn_index - get the connection index of the given NID + * @codec: the HDA codec + * @mux: NID containing the list + * @nid: NID to select + * @recursive: 1 when searching NID recursively, otherwise 0 + * + * Parses the connection list of the widget @mux and checks whether the + * widget @nid is present. If it is, return the connection index. + * Otherwise it returns -1. + */ +int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid, int recursive) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + if (!recursive) + return -1; + if (recursive > 5) { + snd_printd("hda_codec: too deep connection for 0x%x\n", nid); + return -1; + } + recursive++; + for (i = 0; i < nums; i++) + if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0) + return i; + return -1; +} +EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); + /** * snd_hda_queue_unsol_event - add an unsolicited event to queue * @bus: the BUS diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 79ef65e3ae14..10d500d2ba33 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -905,6 +905,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp); +int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid, int recursive); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c7b5ca28fa77..7f93739b1e33 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -346,21 +346,15 @@ static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t pins[2]; unsigned int type; - int j, nums; + int idx; type = get_wcaps_type(get_wcaps(codec, nid)); if (type != AC_WID_AUD_IN) continue; - nums = snd_hda_get_connections(codec, nid, pins, - ARRAY_SIZE(pins)); - if (nums <= 0) - continue; - for (j = 0; j < nums; j++) { - if (pins[j] == pin) { - *idxp = j; - return nid; - } + idx = snd_hda_get_conn_index(codec, nid, pin, 0); + if (idx >= 0) { + *idxp = idx; + return nid; } } return 0; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 9eaf99b01aec..08af4847c07a 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -403,7 +403,6 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi /* clear the table, only one c-media dac assumed here */ memset(spec->multi_init, 0, sizeof(spec->multi_init)); for (j = 0, i = 0; i < cfg->line_outs; i++) { - hda_nid_t conn[4]; nid = cfg->line_out_pins[i]; /* set as output */ spec->multi_init[j].nid = nid; @@ -416,12 +415,10 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; spec->multi_init[j].param = 0; /* find the index in connect list */ - len = snd_hda_get_connections(codec, nid, conn, 4); - for (k = 0; k < len; k++) - if (conn[k] == spec->dac_nids[i]) { - spec->multi_init[j].param = k; - break; - } + k = snd_hda_get_conn_index(codec, nid, + spec->dac_nids[i], 0); + if (k >= 0) + spec->multi_init[j].param = k; j++; } } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 6e864276b744..40cf7f16f587 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3308,19 +3308,8 @@ static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; -/* get the connection index of @nid in the widget @mux */ -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - return -1; -} +#define get_connection_index(codec, mux, nid)\ + snd_hda_get_conn_index(codec, mux, nid, 0) /* get an unassigned DAC from the given list. * Return the nid if found and reduce the DAC list, or return zero if diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cf383ede281d..7b96dcef2c62 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1195,18 +1195,8 @@ static void alc_line_automute(struct hda_codec *codec) update_speakers(codec); } -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - return -1; -} +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) /* switch the current ADC according to the jack state */ static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 7407095cbc78..56425a53cf1b 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3408,30 +3408,9 @@ static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux, return 0; } -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - if (!(get_wcaps(codec, mux) & AC_WCAP_CONN_LIST)) - return -1; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - - for (i = 0; i < nums; i++) { - unsigned int wid_caps = get_wcaps(codec, conn[i]); - unsigned int wid_type = get_wcaps_type(wid_caps); - - if (wid_type != AC_WID_PIN && wid_type != AC_WID_AUD_MIX) - if (get_connection_index(codec, conn[i], nid) >= 0) - return i; - } - return -1; -} +/* look for NID recursively */ +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 1) /* create a volume assigned to the given pin (only if supported) */ /* return 1 if the volume control is created */ diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 997b7057a549..76142c1389d7 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -410,27 +410,8 @@ static int via_new_analog_input(struct via_spec *spec, const char *ctlname, return 0; } -/* return the index of the given widget nid as the source of mux; - * return -1 if not found; - * if num_conns is non-NULL, set the total number of connections - */ -static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid, int *num_conns) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - if (num_conns) - *num_conns = nums; - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - return -1; -} - #define get_connection_index(codec, mux, nid) \ - __get_connection_index(codec, mux, nid, NULL) + snd_hda_get_conn_index(codec, mux, nid, 0) static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int mask) @@ -2011,23 +1992,10 @@ static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) spec->loopback.amplist = spec->loopback_list; } -/* check whether the path from src to dst is reachable */ static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, - hda_nid_t dst, int depth) + hda_nid_t dst) { - hda_nid_t conn[8]; - int i, nums; - - nums = snd_hda_get_connections(codec, src, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == dst) - return true; - if (++depth > MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) - if (is_reachable_nid(codec, conn[i], dst, depth)) - return true; - return false; + return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; } /* add the input-route to the given pin */ @@ -2046,7 +2014,7 @@ static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) continue; spec->inputs[spec->num_inputs].mux_idx = idx; } else { - if (!is_reachable_nid(codec, spec->adc_nids[c], pin, 0)) + if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) continue; } spec->inputs[spec->num_inputs].adc_idx = c; -- cgit v1.2.3 From c82693db52beced0419cecf09a3c81adfe95a544 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Jun 2011 14:17:17 +0200 Subject: ALSA: hda - Enable auto-parser as default for Conexant codecs Let's use auto-parser as default now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 6e90b6b526bc..4ca880bb68fa 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1124,10 +1124,8 @@ static int patch_cxt5045(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, cxt5045_models, cxt5045_cfg_tbl); -#if 0 /* use the old method just for safety */ if (board_config < 0) - board_config = CXT5045_AUTO; -#endif + board_config = CXT5045_AUTO; /* model=auto as default */ if (board_config == CXT5045_AUTO) return patch_conexant_auto(codec); @@ -1565,10 +1563,8 @@ static int patch_cxt5047(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5047_MODELS, cxt5047_models, cxt5047_cfg_tbl); -#if 0 /* not enabled as default, as BIOS often broken for this codec */ if (board_config < 0) - board_config = CXT5047_AUTO; -#endif + board_config = CXT5047_AUTO; /* model=auto as default */ if (board_config == CXT5047_AUTO) return patch_conexant_auto(codec); @@ -1994,10 +1990,8 @@ static int patch_cxt5051(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, cxt5051_models, cxt5051_cfg_tbl); -#if 0 /* use the old method just for safety */ if (board_config < 0) - board_config = CXT5051_AUTO; -#endif + board_config = CXT5051_AUTO; /* model=auto as default */ if (board_config == CXT5051_AUTO) return patch_conexant_auto(codec); @@ -3115,10 +3109,8 @@ static int patch_cxt5066(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, cxt5066_models, cxt5066_cfg_tbl); -#if 0 /* use the old method just for safety */ if (board_config < 0) - board_config = CXT5066_AUTO; -#endif + board_config = CXT5066_AUTO; /* model=auto as default */ if (board_config == CXT5066_AUTO) return patch_conexant_auto(codec); -- cgit v1.2.3 From 94230c11da649866b5b38039d84579d668b6a560 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Jun 2011 17:00:33 +0200 Subject: ALSA: hda - Fix unused variable warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/pci/hda/patch_cmedia.c: In function ‘cmi9880_fill_multi_init’: sound/pci/hda/patch_cmedia.c:401:15: warning: unused variable ‘len’ Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cmedia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 08af4847c07a..cd2cf5e94e81 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -398,7 +398,7 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi { struct cmi_spec *spec = codec->spec; hda_nid_t nid; - int i, j, k, len; + int i, j, k; /* clear the table, only one c-media dac assumed here */ memset(spec->multi_init, 0, sizeof(spec->multi_init)); -- cgit v1.2.3 From e322a36d3998f7f53c76e25e32302632326ec224 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Wed, 29 Jun 2011 13:52:02 +0800 Subject: ALSA: hda - Fix jack-detection on non-VT1708 VIA codecs Move codec init verb which is only applicatable for VT1708. I've found the root cause that jack plugged in can't be detected. The verb in vt1708_init_verbs is used to power down jack detect circuit. This verb is only applicable to VT1708. vt1708 didn't implement jack detect function in hardware, so we should shut down this function to avoid noise. But for other codecs, hardware implement jack detect function. If sending this verb during initialization, jack detect will be invalid. So I move this verb from via_parse_auto_config() to patch_vt1708(). Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 76142c1389d7..93fcea045e3b 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2338,7 +2338,6 @@ static int via_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) spec->mixers[spec->num_mixers++] = spec->kctls.list; - spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; if (spec->hp_dac_nid && spec->hp_dep_path.depth) { err = via_hp_build(codec); @@ -2504,6 +2503,8 @@ static int patch_vt1708(struct hda_codec *codec) if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; + spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; + codec->patch_ops = via_patch_ops; INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); -- cgit v1.2.3 From 2525050518496dfd6905abfa8d6d34288eed36d7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Jun 2011 17:24:47 +0200 Subject: ALSA: hda - Re-implementation of VIA Independent-HP sharing with side stream This patch adds the re-implementation of Independent-HP mode in the case where the DAC is shared between HP and side-channel streams. Now the driver tries to parse the output-path using the pre-parsed side-channel DAC for the independent HP output, too. When a playback PCM stream is opened with this shared mode, the Independent-HP mixer switch can't be changed for avoiding the conflict, thus it returns -EBUSY error. One remaining unintuitive issue is that the DAC volume is still controlled as "Side" volume although it's shared by both independent-HP and side streams. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 93fcea045e3b..5ef14dd7a568 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -128,6 +128,7 @@ struct via_spec { struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; hda_nid_t hp_dac_nid; + bool hp_indep_shared; /* indep HP-DAC is shared with side ch */ int num_active_streams; struct nid_path out_path[4]; @@ -714,19 +715,33 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; + int cur; - spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0]; - if (spec->hp_independent_mode) { + /* no independent-hp status change during PCM playback is running */ + if (spec->num_active_streams) + return -EBUSY; + + cur = !!ucontrol->value.enumerated.item[0]; + if (spec->hp_independent_mode == cur) + return 0; + spec->hp_independent_mode = cur; + if (cur) { activate_output_path(codec, &spec->hp_dep_path, false, false); activate_output_path(codec, &spec->hp_path, true, false); + if (spec->hp_indep_shared) + activate_output_path(codec, &spec->out_path[HDA_SIDE], + false, false); } else { activate_output_path(codec, &spec->hp_path, false, false); activate_output_path(codec, &spec->hp_dep_path, true, false); + if (spec->hp_indep_shared) + activate_output_path(codec, &spec->out_path[HDA_SIDE], + true, false); } /* update jack power state */ set_widgets_power_state(codec); - return 0; + return 1; } static const struct snd_kcontrol_new via_hp_mixer = { @@ -942,10 +957,19 @@ static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; int err; - if (!spec->hp_independent_mode) - spec->multiout.hp_nid = spec->hp_dac_nid; + spec->multiout.hp_nid = 0; + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + if (!spec->hp_independent_mode) { + if (!spec->hp_indep_shared) + spec->multiout.hp_nid = spec->hp_dac_nid; + } else { + if (spec->hp_indep_shared) + spec->multiout.num_dacs = cfg->line_outs - 1; + } + spec->multiout.max_channels = spec->multiout.num_dacs * 2; set_stream_active(codec, true); err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); @@ -1815,13 +1839,20 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) if (parse_output_path(codec, pin, 0, &spec->hp_path)) spec->hp_dac_nid = spec->hp_path.path[0]; + else if (spec->multiout.dac_nids[HDA_SIDE] && + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_SIDE], + &spec->hp_path)) { + spec->hp_dac_nid = spec->hp_path.path[0]; + spec->hp_indep_shared = true; + } if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], &spec->hp_dep_path) && !spec->hp_dac_nid) return 0; - if (spec->hp_dac_nid) + if (spec->hp_dac_nid && !spec->hp_indep_shared) path = &spec->hp_path; else path = &spec->hp_dep_path; -- cgit v1.2.3 From 350434ee53f39adb5e73320be4c98010b87d3dbf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Jun 2011 21:29:12 +0200 Subject: ALSA: hda - Fix missing initialization in alc662 auto-parser A missing initialization resulted in wrong DAC assignments in ALC662 (and other) auto-parsers. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7b96dcef2c62..757a8a3d1e52 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -18780,7 +18780,7 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; - bool redone; + bool redone = false; int i; again: -- cgit v1.2.3 From e5e14681404ec27a422d635284bf564dabde3f81 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Fri, 1 Jul 2011 10:55:07 +0800 Subject: ALSA: hda - Fix the silent front with independent-HP for VIA codecs Unmute DAC on front speaker path when Independent HP is enabled. When to enable Independent HP, the front speaker won't output any sound for VT1708, VT1708B, VT1708S and VT1702. I find the via_independent_hp_put() routine will mute DAC 0 path in Mixer 0. For these codecs, when using Independent HP, there could have two independent streams, one is from DAC0-->Mixer0-->Front Pin, the other is from DAC3-->GainSW3-->Side Pin. So I added a check for DAC-->Mixer path in activate_output_path(). If current path is DAC-->Mixer, no need to mute DAC index in Mixer. In fact, to change connection of Headphone pin or Mux connected with HP is enough. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5ef14dd7a568..bbbc4f4cbf1a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -451,6 +451,9 @@ static void activate_output_path(struct hda_codec *codec, struct nid_path *path, if (enable && path->multi[i]) snd_hda_codec_write(codec, dst, 0, AC_VERB_SET_CONNECT_SEL, idx); + if (get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT && + get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX) + continue; if (have_mute(codec, dst, HDA_INPUT)) { int val = enable ? AMP_IN_UNMUTE(idx) : AMP_IN_MUTE(idx); -- cgit v1.2.3 From c4394f5b807289c180a486df70c1a9b1f192f1cb Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Mon, 4 Jul 2011 16:54:15 +0800 Subject: ALSA: hda - Fix issue that front can't output sound for VT1718S For VT1718S, Mixer 9 doesn't expose the connection to DAC 0. So when building up a 'PCM Playback' amplifier control, it will fail since getting DAC 0 index of Mixer 9 returned -1. So I added a dac_mixer_idx to indicated the actual index of DAC 0 to Mixer 9. Following is the patch and next mail is another. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index bbbc4f4cbf1a..89dd29db97e3 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -130,6 +130,7 @@ struct via_spec { hda_nid_t hp_dac_nid; bool hp_indep_shared; /* indep HP-DAC is shared with side ch */ int num_active_streams; + int dac_mixer_idx; struct nid_path out_path[4]; struct nid_path hp_path; @@ -1810,6 +1811,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) idx = get_connection_index(codec, spec->aa_mix_nid, spec->multiout.dac_nids[0]); + if (idx < 0 && spec->dac_mixer_idx) + idx = spec->dac_mixer_idx; if (idx >= 0) { /* add control to mixer */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, @@ -2959,6 +2962,7 @@ static int patch_vt1718S(struct hda_codec *codec) spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); + spec->dac_mixer_idx = 5; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); -- cgit v1.2.3 From b89596a160dc63043be3fda8babbca9a935af0aa Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Mon, 4 Jul 2011 17:01:33 +0800 Subject: ALSA: hda - Fix invalid multi-channel amplifiers for VT1718S For VT1718S, the multi-channel path should be like following: DAC 0-->Mixer 9(index 5)-->Mixer 0(index 1)-->Front Pin; DAC 1-->Mixer 1(index 0)-->Surround Pin; DAC 2-->C/LFE Pin; DAC 3-->Mixer 2(index 0)-->Side Pin; But current code built Surround and Side path through index 1 of Mixer 1 and 2. So Adjusting Surround and Side channel amplifier is invalid. This patch fixes the issue. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 89dd29db97e3..7305f4de07ec 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -452,8 +452,9 @@ static void activate_output_path(struct hda_codec *codec, struct nid_path *path, if (enable && path->multi[i]) snd_hda_codec_write(codec, dst, 0, AC_VERB_SET_CONNECT_SEL, idx); - if (get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT && - get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX) + if (!force + && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT + && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX) continue; if (have_mute(codec, dst, HDA_INPUT)) { int val = enable ? AMP_IN_UNMUTE(idx) : @@ -490,8 +491,8 @@ static void via_auto_init_output(struct hda_codec *codec, { struct via_spec *spec = codec->spec; unsigned int caps; - hda_nid_t pin, nid; - int i, idx; + hda_nid_t pin, nid, pre_nid; + int i, idx, j, num; if (!path->depth) return; @@ -513,12 +514,26 @@ static void via_auto_init_output(struct hda_codec *codec, return; for (i = path->depth - 1; i > 0; i--) { nid = path->path[i]; + pre_nid = path->path[i - 1]; idx = get_connection_index(codec, nid, spec->aa_mix_nid); if (idx >= 0) { - if (have_mute(codec, nid, HDA_INPUT)) + if (have_mute(codec, nid, HDA_INPUT)) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(idx)); + if (pre_nid == spec->multiout.dac_nids[0]) { + num = snd_hda_get_conn_list(codec, nid, + NULL); + for (j = 0; j < num; j++) { + if (j == idx) + continue; + snd_hda_codec_write(codec, + nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(j)); + } + } + } break; } } -- cgit v1.2.3 From de6c74f3e323b132caec898d224e0e3253d92eaf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Jul 2011 14:46:42 +0200 Subject: ALSA: hda - Define some constants in patch_via.c Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 7305f4de07ec..0a5a02ac2b22 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -107,6 +107,8 @@ struct via_input { const char *label; /* input-source label */ }; +#define VIA_MAX_ADCS 3 + struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; @@ -132,15 +134,15 @@ struct via_spec { int num_active_streams; int dac_mixer_idx; - struct nid_path out_path[4]; + struct nid_path out_path[HDA_SIDE + 1]; struct nid_path hp_path; struct nid_path hp_dep_path; struct nid_path speaker_path; /* capture */ unsigned int num_adc_nids; - hda_nid_t adc_nids[3]; - hda_nid_t mux_nids[3]; + hda_nid_t adc_nids[VIA_MAX_ADCS]; + hda_nid_t mux_nids[VIA_MAX_ADCS]; hda_nid_t aa_mix_nid; hda_nid_t dig_in_nid; @@ -148,7 +150,7 @@ struct via_spec { bool dyn_adc_switch; int num_inputs; struct via_input inputs[AUTO_CFG_MAX_INS + 1]; - unsigned int cur_mux[3]; + unsigned int cur_mux[VIA_MAX_ADCS]; /* dynamic ADC switching */ hda_nid_t cur_adc; -- cgit v1.2.3 From 18bd2c44b9c7f0ee775e756dd59e12e0939f7ab9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Jul 2011 15:55:44 +0200 Subject: ALSA: hda - Create HP-vol control properly for VIA codecs When the individual DAC is available for the headphone output, the driver should create the DAC for its volume control. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 0a5a02ac2b22..8d46a0f937a9 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1855,6 +1855,7 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; struct nid_path *path; + bool check_dac; int err; if (!pin) @@ -1875,11 +1876,14 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) !spec->hp_dac_nid) return 0; - if (spec->hp_dac_nid && !spec->hp_indep_shared) + if (spec->hp_dac_nid && !spec->hp_indep_shared) { path = &spec->hp_path; - else + check_dac = true; + } else { path = &spec->hp_dep_path; - err = create_ch_ctls(codec, "Headphone", 3, false, path); + check_dac = false; + } + err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); if (err < 0) return err; if (spec->hp_dac_nid) { -- cgit v1.2.3 From bac4b92cf7a444c0af8dd7b269c8791595c44052 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Jul 2011 17:35:51 +0200 Subject: ALSA: hda - Don't add aa-mix for VIA surrounds Since we now route the front DAC via aa-mix widget, adding the aa-mix to surrounds will result in a mix-up of both front and surround PCM signals. For avoiding this, the aa-mix routes have to be disabled for surround paths. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 8d46a0f937a9..42d5a91781fc 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -489,7 +489,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, static void via_auto_init_output(struct hda_codec *codec, struct nid_path *path, int pin_type, - bool force) + bool with_aa_mix, bool force) { struct via_spec *spec = codec->spec; unsigned int caps; @@ -520,9 +520,12 @@ static void via_auto_init_output(struct hda_codec *codec, idx = get_connection_index(codec, nid, spec->aa_mix_nid); if (idx >= 0) { if (have_mute(codec, nid, HDA_INPUT)) { + unsigned int mute = with_aa_mix ? + AMP_IN_UNMUTE(idx) : AMP_IN_MUTE(idx); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(idx)); + mute); + /* exclusively via aa-mix for front */ if (pre_nid == spec->multiout.dac_nids[0]) { num = snd_hda_get_conn_list(codec, nid, NULL); @@ -547,7 +550,9 @@ static void via_auto_init_multi_out(struct hda_codec *codec) int i; for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) - via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true); + /* enable aa-mute only for the front channel */ + via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, + i == 0, true); } static void via_auto_init_hp_out(struct hda_codec *codec) @@ -555,15 +560,18 @@ static void via_auto_init_hp_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (!spec->hp_dac_nid) { - via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, + true, true); return; } if (spec->hp_independent_mode) { activate_output_path(codec, &spec->hp_dep_path, false, false); - via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_path, PIN_HP, + true, true); } else { activate_output_path(codec, &spec->hp_path, false, false); - via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, + true, true); } } @@ -572,7 +580,8 @@ static void via_auto_init_speaker_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (spec->autocfg.speaker_outs) - via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, + true, true); } static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); -- cgit v1.2.3 From b68785714b67079385188323631b05a8f9093675 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Wed, 6 Jul 2011 09:51:29 +0200 Subject: ALSA: hda - Add Realtek ALC269VC codec support Add the support of ALC269VC codec. Also delete the unnecessary codec_variant type enum list: now only three variants (ALC269VA ALC269VB ALC269VC) are needed. In addition, added some aliases: - Add ALC269VB alias name ALC277 - Add ALC269VC alias name ALC259 ALC281X - Add ALC269VC for Lenovo device 0x21f3 name ALC3202 Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 757a8a3d1e52..575ffc9fedbd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14650,12 +14650,9 @@ static int alc275_setup_dual_adc(struct hda_codec *codec) /* different alc269-variants */ enum { - ALC269_TYPE_NORMAL, - ALC269_TYPE_ALC258, - ALC269_TYPE_ALC259, + ALC269_TYPE_ALC269VA, ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC270, - ALC269_TYPE_ALC271X, + ALC269_TYPE_ALC269VC, }; /* @@ -14675,7 +14672,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - if (spec->codec_variant == ALC269_TYPE_NORMAL) + if (spec->codec_variant == ALC269_TYPE_ALC269VA) err = alc269_auto_create_input_ctls(codec, &spec->autocfg); else err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0, @@ -14690,7 +14687,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - if (spec->codec_variant != ALC269_TYPE_NORMAL) + if (spec->codec_variant != ALC269_TYPE_ALC269VA) alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); else alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); @@ -15148,24 +15145,33 @@ static int patch_alc269(struct hda_codec *codec) alc_auto_parse_customize_define(codec); if (codec->vendor_id == 0x10ec0269) { + spec->codec_variant = ALC269_TYPE_ALC269VA; coef = alc_read_coef_idx(codec, 0); if ((coef & 0x00f0) == 0x0010) { if (codec->bus->pci->subsystem_vendor == 0x1025 && spec->cdefine.platform_type == 1) { alc_codec_rename(codec, "ALC271X"); - spec->codec_variant = ALC269_TYPE_ALC271X; - } else if ((coef & 0xf000) == 0x1000) { - spec->codec_variant = ALC269_TYPE_ALC270; } else if ((coef & 0xf000) == 0x2000) { alc_codec_rename(codec, "ALC259"); - spec->codec_variant = ALC269_TYPE_ALC259; } else if ((coef & 0xf000) == 0x3000) { alc_codec_rename(codec, "ALC258"); - spec->codec_variant = ALC269_TYPE_ALC258; + } else if ((coef & 0xfff0) == 0x3010) { + alc_codec_rename(codec, "ALC277"); } else { alc_codec_rename(codec, "ALC269VB"); - spec->codec_variant = ALC269_TYPE_ALC269VB; } + spec->codec_variant = ALC269_TYPE_ALC269VB; + } else if ((coef & 0x00f0) == 0x0020) { + if (coef == 0xa023) + alc_codec_rename(codec, "ALC259"); + else if (coef == 0x6023) + alc_codec_rename(codec, "ALC281X"); + else if (codec->bus->pci->subsystem_vendor == 0x17aa && + codec->bus->pci->subsystem_device == 0x21f3) + alc_codec_rename(codec, "ALC3202"); + else + alc_codec_rename(codec, "ALC269VC"); + spec->codec_variant = ALC269_TYPE_ALC269VC; } else alc_fix_pll_init(codec, 0x20, 0x04, 15); alc269_fill_coef(codec); @@ -15229,7 +15235,7 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_capture = &alc269_pcm_digital_capture; if (!spec->adc_nids) { /* wasn't filled automatically? use default */ - if (spec->codec_variant == ALC269_TYPE_NORMAL) { + if (spec->codec_variant == ALC269_TYPE_ALC269VA) { spec->adc_nids = alc269_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); spec->capsrc_nids = alc269_capsrc_nids; -- cgit v1.2.3 From bb8bf4d40cb67dac12106746067994c38229de69 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 13:07:54 +0200 Subject: ALSA: hda - Parse HP and speaker DACs even for multi connections for ALC662 In alc662_auto_fill_dac_nids(), the HP and speaker DACs aren't parsed when the corresponding pins aren't fixed with single DACs. Now check these DACs even for non-fixed pins. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 49f39699ea1b..3cd21040555a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -18845,6 +18845,13 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec) sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); } + if (cfg->hp_outs && !spec->multiout.hp_nid) + spec->multiout.hp_nid = + alc_auto_look_for_dac(codec, cfg->hp_pins[0]); + if (cfg->speaker_outs && !spec->multiout.extra_out_nid[0]) + spec->multiout.extra_out_nid[0] = + alc_auto_look_for_dac(codec, cfg->speaker_pins[0]); + return 0; } -- cgit v1.2.3 From cd51155676b1c0f44604e304109cb9739a54ea7e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 13:10:42 +0200 Subject: ALSA: hda - Initialize DACs in ALC662 auto-parser mode The initialization of DACs was missing in ALC662 parser code. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3cd21040555a..62853e3bda83 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -18970,26 +18970,38 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t dac) { int i, num; + hda_nid_t mix; hda_nid_t srcs[HDA_MAX_CONNECTIONS]; alc_set_pin_output(codec, nid, pin_type); + nid = alc_go_down_to_selector(codec, nid); num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); for (i = 0; i < num; i++) { if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) continue; - /* need the manual connection? */ - if (num > 1) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, i); - /* unmute mixer widget inputs */ - snd_hda_codec_write(codec, srcs[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, srcs[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - return; + mix = srcs[i]; + break; } + if (!mix) + return; + + /* need the manual connection? */ + if (num > 1) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); + /* unmute mixer widget inputs */ + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + /* initialize volume */ + if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = dac; + else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = mix; + else + return; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); } static void alc662_auto_init_multi_out(struct hda_codec *codec) -- cgit v1.2.3 From 97aaab7b493d9e22a9c0a125a501920354e67846 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 14:02:55 +0200 Subject: ALSA: hda - Create bind-mutes appropriately for ALC662 auto-parser When multiple inputs are present on the mixer widget (typically a DAC and a loopback), mute/unmute both inputs with the corresponding mixer element. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 58 +++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 62853e3bda83..236200963b02 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -18855,28 +18855,38 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec) return 0; } -static inline int __alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) +static int alc662_add_vol_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) { - return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -static inline int __alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) +#define alc662_add_stereo_vol(codec, pfx, cidx, nid) \ + alc662_add_vol_ctl(codec, pfx, cidx, nid, 3) + +/* create a mute-switch for the given mixer widget; + * if it has multiple sources (e.g. DAC and loopback), create a bind-mute + */ +static int alc662_add_sw_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) { - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT)); + int type; + unsigned long val; + if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); + } else { + type = ALC_CTL_BIND_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); + } + return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); } -#define alc662_add_vol_ctl(spec, pfx, nid, chs) \ - __alc662_add_vol_ctl(spec, pfx, nid, 0, chs) -#define alc662_add_sw_ctl(spec, pfx, nid, chs) \ - __alc662_add_sw_ctl(spec, pfx, nid, 0, chs) -#define alc662_add_stereo_vol(spec, pfx, nid) \ - alc662_add_vol_ctl(spec, pfx, nid, 3) -#define alc662_add_stereo_sw(spec, pfx, nid) \ - alc662_add_sw_ctl(spec, pfx, nid, 3) +#define alc662_add_stereo_sw(codec, pfx, cidx, nid) \ + alc662_add_sw_ctl(codec, pfx, cidx, nid, 3) /* add playback controls from the parsed DAC table */ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, @@ -18906,23 +18916,23 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, name = alc_get_line_out_pfx(spec, i, true, &index); if (!name) { /* Center/LFE */ - err = alc662_add_vol_ctl(spec, "Center", nid, 1); + err = alc662_add_vol_ctl(codec, "Center", 0, nid, 1); if (err < 0) return err; - err = alc662_add_vol_ctl(spec, "LFE", nid, 2); + err = alc662_add_vol_ctl(codec, "LFE", 0, nid, 2); if (err < 0) return err; - err = alc662_add_sw_ctl(spec, "Center", mix, 1); + err = alc662_add_sw_ctl(codec, "Center", 0, mix, 1); if (err < 0) return err; - err = alc662_add_sw_ctl(spec, "LFE", mix, 2); + err = alc662_add_sw_ctl(codec, "LFE", 0, mix, 2); if (err < 0) return err; } else { - err = __alc662_add_vol_ctl(spec, name, nid, index, 3); + err = alc662_add_stereo_vol(codec, name, index, nid); if (err < 0) return err; - err = __alc662_add_sw_ctl(spec, name, mix, index, 3); + err = alc662_add_stereo_sw(codec, name, index, mix); if (err < 0) return err; } @@ -18952,10 +18962,10 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, mix = alc_auto_dac_to_mix(codec, pin, dac); if (!mix) return 0; - err = alc662_add_vol_ctl(spec, pfx, dac, 3); + err = alc662_add_stereo_vol(codec, pfx, 0, dac); if (err < 0) return err; - err = alc662_add_sw_ctl(spec, pfx, mix, 3); + err = alc662_add_stereo_sw(codec, pfx, 0, mix); if (err < 0) return err; return 0; -- cgit v1.2.3 From 343a04be376a3733514e4eca7a8c8adb2493ea23 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 14:28:39 +0200 Subject: ALSA: hda - Code consolidation for ALC88x and ALC662 auto-parsers Use the same common code for auto-parsing the output paths and their initializations, based on the existing ALC662 code, which is smarter than the old ALC880/2 code. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 428 ++++++++---------------------------------- 1 file changed, 75 insertions(+), 353 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 236200963b02..a0ed9e524d84 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5325,46 +5325,6 @@ static int add_control_with_pfx(struct alc_spec *spec, int type, #define alc880_idx_to_selector(nid) ((nid) + 0x10) #define ALC880_PIN_CD_NID 0x1c -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc880_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int assigned[4]; - int i, j; - - memset(assigned, 0, sizeof(assigned)); - spec->multiout.dac_nids = spec->private_dac_nids; - - /* check the pins hardwired to audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (alc880_is_fixed_pin(nid)) { - int idx = alc880_fixed_pin_idx(nid); - spec->private_dac_nids[i] = alc880_idx_to_dac(idx); - assigned[idx] = 1; - } - } - /* left pins can be connect to any audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (alc880_is_fixed_pin(nid)) - continue; - /* search for an empty channel */ - for (j = 0; j < cfg->line_outs; j++) { - if (!assigned[j]) { - spec->private_dac_nids[i] = - alc880_idx_to_dac(j); - assigned[j] = 1; - break; - } - } - } - spec->multiout.num_dacs = cfg->line_outs; - return 0; -} - static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, bool can_be_master, int *index) { @@ -5397,106 +5357,6 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return chname[ch]; } -/* add playback controls from the parsed DAC table */ -static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - if (!spec->multiout.dac_nids[i]) - continue; - nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); - name = alc_get_line_out_pfx(spec, i, false, &index); - if (!name) { - /* Center/LFE */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "Center", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "LFE", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "Center", - HDA_COMPOSE_AMP_VAL(nid, 1, 2, - HDA_INPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "LFE", - HDA_COMPOSE_AMP_VAL(nid, 2, 2, - HDA_INPUT)); - if (err < 0) - return err; - } else { - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, index, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, index, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, - HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, - const char *pfx) -{ - hda_nid_t nid; - int err; - - if (!pin) - return 0; - - if (alc880_is_fixed_pin(pin)) { - nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else - spec->multiout.extra_out_nid[0] = nid; - /* control HP volume/switch on the output mixer amp */ - nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } else if (alc880_is_multi_pin(pin)) { - /* set manual connection */ - /* we have only a switch on HP-out PIN */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - /* create input playback/capture controls for the given pin */ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname, int ctlidx, @@ -5569,6 +5429,14 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec, return 0; } +static int alc_auto_fill_dac_nids(struct hda_codec *codec); +static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg); +static int alc_auto_create_hp_out(struct hda_codec *codec); +static int alc_auto_create_speaker_out(struct hda_codec *codec); +static void alc_auto_init_multi_out(struct hda_codec *codec); +static void alc_auto_init_extra_out(struct hda_codec *codec); + static int alc880_auto_create_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { @@ -5585,21 +5453,6 @@ static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, AMP_OUT_UNMUTE); } -static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) -{ - alc_set_pin_output(codec, nid, pin_type); - /* need the manual connection? */ - if (alc880_is_multi_pin(nid)) { - struct alc_spec *spec = codec->spec; - int idx = alc880_multi_pin_idx(nid); - snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, - AC_VERB_SET_CONNECT_SEL, - alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); - } -} - static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -5608,63 +5461,6 @@ static int get_pin_type(int line_out_type) return PIN_OUT; } -static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t dac) -{ - hda_nid_t nid, mix; - - if (!dac) - return; - mix = alc880_idx_to_mixer(alc880_dac_to_idx(dac)); - if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) - nid = dac; - else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) - nid = mix; - else - nid = 0; - if (nid) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - if (query_amp_caps(codec, mix, HDA_INPUT) & AC_AMPCAP_MUTE) { - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } -} - -static void alc880_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc880_auto_set_output_and_unmute(codec, nid, pin_type, i); - } - /* mute DACs */ - for (i = 0; i < spec->multiout.num_dacs; i++) - alc880_auto_init_dac(codec, spec->multiout.dac_nids[i]); -} - -static void alc880_auto_init_extra_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - int i; - - pin = spec->autocfg.speaker_pins[0]; - if (pin) /* connect to front */ - alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); - /* mute DACs */ - alc880_auto_init_dac(codec, spec->multiout.hp_nid); - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) - alc880_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); -} - static void alc880_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5734,22 +5530,19 @@ static int alc880_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(codec); + err = alc_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids); + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; - err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc880_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); + err = alc_auto_create_hp_out(codec); if (err < 0) return err; - err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], - "Headphone"); + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc880_auto_create_input_ctls(codec, &spec->autocfg); @@ -5775,8 +5568,8 @@ static int alc880_parse_auto_config(struct hda_codec *codec) static void alc880_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc880_auto_init_multi_out(codec); - alc880_auto_init_extra_out(codec); + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); alc880_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -10990,82 +10783,6 @@ static int alc882_auto_create_input_ctls(struct hda_codec *codec, return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22); } -static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - hda_nid_t dac) -{ - int idx; - - /* set as output */ - alc_set_pin_output(codec, nid, pin_type); - - if (snd_hda_get_conn_list(codec, nid, NULL) < 2) - return; - - if (dac == 0x25) - idx = 4; - else if (dac >= 0x02 && dac <= 0x05) - idx = dac - 2; - else - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -#define alc882_auto_init_dac alc880_auto_init_dac - -static void alc882_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc882_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } - /* mute DACs */ - for (i = 0; i < spec->multiout.num_dacs; i++) - alc882_auto_init_dac(codec, spec->multiout.dac_nids[i]); -} - -static void alc882_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin, dac; - int i; - - if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) { - for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_nid; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); - } - } - - if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) { - for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[0]; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); - } - } - - /* mute DACs */ - alc882_auto_init_dac(codec, spec->multiout.hp_nid); - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) - alc882_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); -} - #define alc882_auto_init_analog_input alc880_auto_init_analog_input static void alc882_auto_init_input_src(struct hda_codec *codec) @@ -11177,22 +10894,19 @@ static int alc882_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(codec); + err = alc_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids); + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; - err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], - "Headphone"); + err = alc_auto_create_hp_out(codec); if (err < 0) return err; - err = alc880_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc882_auto_create_input_ctls(codec, &spec->autocfg); @@ -11206,10 +10920,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - /* if ADC 0x07 is available, initialize it, too */ - if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN) - add_verb(spec, alc882_adc1_init_verbs); - spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; @@ -11226,8 +10936,8 @@ static int alc882_parse_auto_config(struct hda_codec *codec) static void alc882_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc882_auto_init_multi_out(codec); - alc882_auto_init_hp_out(codec); + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); alc882_auto_init_analog_input(codec); alc882_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -12586,8 +12296,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc262_auto_init_multi_out alc882_auto_init_multi_out -#define alc262_auto_init_hp_out alc882_auto_init_hp_out #define alc262_auto_init_analog_input alc882_auto_init_analog_input #define alc262_auto_init_input_src alc882_auto_init_input_src @@ -12596,8 +12304,8 @@ static int alc262_parse_auto_config(struct hda_codec *codec) static void alc262_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc262_auto_init_multi_out(codec); - alc262_auto_init_hp_out(codec); + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); alc262_auto_init_analog_input(codec); alc262_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -16970,8 +16678,6 @@ static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, } -#define alc861vd_auto_init_multi_out alc882_auto_init_multi_out -#define alc861vd_auto_init_hp_out alc882_auto_init_hp_out #define alc861vd_auto_init_analog_input alc882_auto_init_analog_input #define alc861vd_auto_init_input_src alc882_auto_init_input_src @@ -17110,10 +16816,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(codec); + err = alc_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids); + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); @@ -17156,8 +16862,8 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) static void alc861vd_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc861vd_auto_init_multi_out(codec); - alc861vd_auto_init_hp_out(codec); + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); alc861vd_auto_init_analog_input(codec); alc861vd_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -18795,7 +18501,7 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) } /* fill in the dac_nids table from the parsed pin configuration */ -static int alc662_auto_fill_dac_nids(struct hda_codec *codec) +static int alc_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; @@ -18855,7 +18561,7 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec) return 0; } -static int alc662_add_vol_ctl(struct hda_codec *codec, +static int alc_auto_add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, hda_nid_t nid, unsigned int chs) { @@ -18863,13 +18569,13 @@ static int alc662_add_vol_ctl(struct hda_codec *codec, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -#define alc662_add_stereo_vol(codec, pfx, cidx, nid) \ - alc662_add_vol_ctl(codec, pfx, cidx, nid, 3) +#define alc_auto_add_stereo_vol(codec, pfx, cidx, nid) \ + alc_auto_add_vol_ctl(codec, pfx, cidx, nid, 3) /* create a mute-switch for the given mixer widget; * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ -static int alc662_add_sw_ctl(struct hda_codec *codec, +static int alc_auto_add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, hda_nid_t nid, unsigned int chs) { @@ -18885,11 +18591,11 @@ static int alc662_add_sw_ctl(struct hda_codec *codec, return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); } -#define alc662_add_stereo_sw(codec, pfx, cidx, nid) \ - alc662_add_sw_ctl(codec, pfx, cidx, nid, 3) +#define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \ + alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3) /* add playback controls from the parsed DAC table */ -static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, +static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct alc_spec *spec = codec->spec; @@ -18916,23 +18622,23 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, name = alc_get_line_out_pfx(spec, i, true, &index); if (!name) { /* Center/LFE */ - err = alc662_add_vol_ctl(codec, "Center", 0, nid, 1); + err = alc_auto_add_vol_ctl(codec, "Center", 0, nid, 1); if (err < 0) return err; - err = alc662_add_vol_ctl(codec, "LFE", 0, nid, 2); + err = alc_auto_add_vol_ctl(codec, "LFE", 0, nid, 2); if (err < 0) return err; - err = alc662_add_sw_ctl(codec, "Center", 0, mix, 1); + err = alc_auto_add_sw_ctl(codec, "Center", 0, mix, 1); if (err < 0) return err; - err = alc662_add_sw_ctl(codec, "LFE", 0, mix, 2); + err = alc_auto_add_sw_ctl(codec, "LFE", 0, mix, 2); if (err < 0) return err; } else { - err = alc662_add_stereo_vol(codec, name, index, nid); + err = alc_auto_add_stereo_vol(codec, name, index, nid); if (err < 0) return err; - err = alc662_add_stereo_sw(codec, name, index, mix); + err = alc_auto_add_stereo_sw(codec, name, index, mix); if (err < 0) return err; } @@ -18941,7 +18647,7 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, } /* add playback controls for speaker and HP outputs */ -static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, +static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac, const char *pfx) { struct alc_spec *spec = codec->spec; @@ -18962,25 +18668,41 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, mix = alc_auto_dac_to_mix(codec, pin, dac); if (!mix) return 0; - err = alc662_add_stereo_vol(codec, pfx, 0, dac); + err = alc_auto_add_stereo_vol(codec, pfx, 0, dac); if (err < 0) return err; - err = alc662_add_stereo_sw(codec, pfx, 0, mix); + err = alc_auto_add_stereo_sw(codec, pfx, 0, mix); if (err < 0) return err; return 0; } +static int alc_auto_create_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + spec->multiout.hp_nid, + "Headphone"); +} + +static int alc_auto_create_speaker_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], + spec->multiout.extra_out_nid[0], + "Speaker"); +} + /* create playback/capture controls for input pins */ #define alc662_auto_create_input_ctls \ alc882_auto_create_input_ctls -static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, hda_nid_t dac) { int i, num; - hda_nid_t mix; + hda_nid_t mix = 0; hda_nid_t srcs[HDA_MAX_CONNECTIONS]; alc_set_pin_output(codec, nid, pin_type); @@ -19014,7 +18736,7 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, AMP_OUT_ZERO); } -static void alc662_auto_init_multi_out(struct hda_codec *codec) +static void alc_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -19023,23 +18745,23 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec) for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) - alc662_auto_set_output_and_unmute(codec, nid, pin_type, + alc_auto_set_output_and_unmute(codec, nid, pin_type, spec->multiout.dac_nids[i]); } } -static void alc662_auto_init_hp_out(struct hda_codec *codec) +static void alc_auto_init_extra_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t pin; pin = spec->autocfg.hp_pins[0]; if (pin) - alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.hp_nid); pin = spec->autocfg.speaker_pins[0]; if (pin) - alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, spec->multiout.extra_out_nid[0]); } @@ -19226,22 +18948,22 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc662_auto_fill_dac_nids(codec); + err = alc_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec, alc662_auto_fill_dac_nids); + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; - err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg); + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc662_auto_create_extra_out(codec, + err = alc_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], spec->multiout.extra_out_nid[0], "Speaker"); if (err < 0) return err; - err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + err = alc_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], spec->multiout.hp_nid, "Headphone"); if (err < 0) @@ -19277,8 +18999,8 @@ static int alc662_parse_auto_config(struct hda_codec *codec) static void alc662_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc662_auto_init_multi_out(codec); - alc662_auto_init_hp_out(codec); + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); alc662_auto_init_analog_input(codec); alc662_auto_init_input_src(codec); alc_auto_init_digital(codec); -- cgit v1.2.3 From b78217096bcf60f2248fe2a5449a7e4f7fb29105 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 15:12:46 +0200 Subject: ALSA: hda - Parse ADCs in alc_auto_create_input_ctls() Parse ADCs and cap-srcs in alc_auto_create_input_ctls() by itself instead of passing explicitly from the caller. By this change, all alc*_auto_create_input_ctls() can be unified to the same calls. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 141 ++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 74 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a0ed9e524d84..45532eb37cba 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5381,17 +5381,63 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) return (pincap & AC_PINCAP_IN) != 0; } +static int alc_auto_fill_adc_caps(struct hda_codec *codec, hda_nid_t *adc_nids, + hda_nid_t *cap_nids, int max_nums) +{ + hda_nid_t nid; + int i, nums = 0; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + hda_nid_t src; + const hda_nid_t *list; + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + cap_nids[nums] = nid; + src = nid; + for (;;) { + int n; + type = get_wcaps_type(get_wcaps(codec, src)); + if (type == AC_WID_PIN) + break; + if (type == AC_WID_AUD_SEL) { + cap_nids[nums] = src; + break; + } + n = snd_hda_get_conn_list(codec, src, &list); + if (n > 1) { + cap_nids[nums] = src; + break; + } else if (n != 1) + break; + src = *list; + } + if (++nums >= max_nums) + break; + } + return nums; +} + /* create playback/capture controls for input pins */ -static int alc_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - hda_nid_t mixer, - hda_nid_t cap1, hda_nid_t cap2) +static int alc_auto_create_input_ctls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, type_idx = 0; + int num_adcs; + hda_nid_t caps[5], adcs[5]; + int i, c, err, idx, type_idx = 0; const char *prev_label = NULL; + num_adcs = alc_auto_fill_adc_caps(codec, adcs, caps, ARRAY_SIZE(adcs)); + if (num_adcs < 0) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; @@ -5418,13 +5464,13 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec, } } - if (!cap1) - continue; - idx = get_connection_index(codec, cap1, pin); - if (idx < 0 && cap2) - idx = get_connection_index(codec, cap2, pin); - if (idx >= 0) - snd_hda_add_imux_item(imux, label, idx, NULL); + for (c = 0; c < num_adcs; c++) { + idx = get_connection_index(codec, caps[c], pin); + if (idx >= 0) { + snd_hda_add_imux_item(imux, label, idx, NULL); + break; + } + } } return 0; } @@ -5437,12 +5483,6 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec); static void alc_auto_init_multi_out(struct hda_codec *codec); static void alc_auto_init_extra_out(struct hda_codec *codec); -static int alc880_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09); -} - static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_type) { @@ -5545,7 +5585,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec) err = alc_auto_create_speaker_out(codec); if (err < 0) return err; - err = alc880_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -7057,13 +7097,6 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, return 0; } -/* create playback/capture controls for input pins */ -static int alc260_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05); -} - static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int sel_idx) @@ -7127,7 +7160,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) return err; if (!spec->kctls.list) return 0; /* can't find valid BIOS pin config */ - err = alc260_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -10777,12 +10810,6 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { /* * BIOS auto configuration */ -static int alc882_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22); -} - #define alc882_auto_init_analog_input alc880_auto_init_analog_input static void alc882_auto_init_input_src(struct hda_codec *codec) @@ -10909,7 +10936,7 @@ static int alc882_parse_auto_config(struct hda_codec *codec) err = alc_auto_create_speaker_out(codec); if (err < 0) return err; - err = alc882_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -11984,9 +12011,6 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, return 0; } -#define alc262_auto_create_input_ctls \ - alc882_auto_create_input_ctls - static const struct hda_verb alc262_HP_BPC_init_verbs[] = { /* * Unmute ADC0-2 and set the default input to mic-in @@ -12272,7 +12296,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec) err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - err = alc262_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -13285,13 +13309,6 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, return 0; } -/* create playback/capture controls for input pins */ -static int alc268_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24); -} - static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type) { @@ -13429,7 +13446,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - err = alc268_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -14276,8 +14293,6 @@ static const struct hda_verb alc269vb_init_verbs[] = { #define alc269_auto_create_multi_out_ctls \ alc268_auto_create_multi_out_ctls -#define alc269_auto_create_input_ctls \ - alc268_auto_create_input_ctls #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc269_loopbacks alc880_loopbacks @@ -14393,11 +14408,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - if (spec->codec_variant == ALC269_TYPE_ALC269VA) - err = alc269_auto_create_input_ctls(codec, &spec->autocfg); - else - err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0, - 0x22, 0); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -15671,13 +15682,6 @@ static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) return 0; } -/* create playback/capture controls for input pins */ -static int alc861_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0); -} - static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, hda_nid_t dac) @@ -15766,7 +15770,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec) err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = alc861_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -16671,13 +16675,6 @@ static const struct alc_config_preset alc861vd_presets[] = { /* * BIOS auto configuration */ -static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x22, 0); -} - - #define alc861vd_auto_init_analog_input alc882_auto_init_analog_input #define alc861vd_auto_init_input_src alc882_auto_init_input_src @@ -16835,7 +16832,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) "Headphone"); if (err < 0) return err; - err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -18693,10 +18690,6 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); } -/* create playback/capture controls for input pins */ -#define alc662_auto_create_input_ctls \ - alc882_auto_create_input_ctls - static void alc_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, hda_nid_t dac) @@ -18968,7 +18961,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec) "Headphone"); if (err < 0) return err; - err = alc662_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; -- cgit v1.2.3 From 0a7f532090f04093f8c11c4679e892f3a0fb63e5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 15:15:12 +0200 Subject: ALSA: hda - Unify alc_auto_init_analog_input() calls All alc*_auto_init_analog_input() calls are identical, so let's use the same function more clearly without aliases. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 45532eb37cba..c70d3469ab29 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5501,7 +5501,7 @@ static int get_pin_type(int line_out_type) return PIN_OUT; } -static void alc880_auto_init_analog_input(struct hda_codec *codec) +static void alc_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; @@ -5610,7 +5610,7 @@ static void alc880_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); - alc880_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc880_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -7142,7 +7142,6 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); } -#define alc260_auto_init_analog_input alc880_auto_init_analog_input #define alc260_auto_init_input_src alc880_auto_init_input_src static int alc260_parse_auto_config(struct hda_codec *codec) @@ -7184,7 +7183,7 @@ static void alc260_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; alc260_auto_init_multi_out(codec); - alc260_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc260_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -10810,8 +10809,6 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { /* * BIOS auto configuration */ -#define alc882_auto_init_analog_input alc880_auto_init_analog_input - static void alc882_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -10965,7 +10962,7 @@ static void alc882_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); - alc882_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc882_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -12320,7 +12317,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc262_auto_init_analog_input alc882_auto_init_analog_input #define alc262_auto_init_input_src alc882_auto_init_input_src @@ -12330,7 +12326,7 @@ static void alc262_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); - alc262_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc262_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -13475,7 +13471,6 @@ static int alc268_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc268_auto_init_analog_input alc882_auto_init_analog_input #define alc268_auto_init_input_src alc882_auto_init_input_src /* init callback for auto-configuration model -- overriding the default init */ @@ -13485,7 +13480,7 @@ 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); + alc_auto_init_analog_input(codec); alc268_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -14443,7 +14438,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) #define alc269_auto_init_multi_out alc268_auto_init_multi_out #define alc269_auto_init_hp_out alc268_auto_init_hp_out -#define alc269_auto_init_analog_input alc882_auto_init_analog_input #define alc269_auto_init_input_src alc882_auto_init_input_src @@ -14453,7 +14447,7 @@ static void alc269_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc269_auto_init_multi_out(codec); alc269_auto_init_hp_out(codec); - alc269_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); if (!spec->dual_adc_switch) alc269_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -15739,8 +15733,6 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) spec->multiout.dac_nids[0]); } -#define alc861_auto_init_analog_input alc880_auto_init_analog_input - /* parse the BIOS configuration and set up the alc_spec */ /* return 1 if successful, 0 if the proper config is not found, * or a negative error code @@ -15799,7 +15791,7 @@ static void alc861_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc861_auto_init_multi_out(codec); alc861_auto_init_hp_out(codec); - alc861_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -16675,7 +16667,6 @@ static const struct alc_config_preset alc861vd_presets[] = { /* * BIOS auto configuration */ -#define alc861vd_auto_init_analog_input alc882_auto_init_analog_input #define alc861vd_auto_init_input_src alc882_auto_init_input_src #define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) @@ -16861,7 +16852,7 @@ static void alc861vd_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); - alc861vd_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc861vd_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -18758,7 +18749,6 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) spec->multiout.extra_out_nid[0]); } -#define alc662_auto_init_analog_input alc882_auto_init_analog_input #define alc662_auto_init_input_src alc882_auto_init_input_src /* @@ -18994,7 +18984,7 @@ static void alc662_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); - alc662_auto_init_analog_input(codec); + alc_auto_init_analog_input(codec); alc662_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) @@ -19552,15 +19542,13 @@ static int alc680_parse_auto_config(struct hda_codec *codec) 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); + alc_auto_init_analog_input(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); -- cgit v1.2.3 From d6cc9fabd58f33e829a3182aa856db0d57c726ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 16:38:42 +0200 Subject: ALSA: hda - Parse ADCs and CAPSRCs dynamically for Realtek auto-parser Now with the new code for looking for ADCs and MUXs, we can replace the whole ADC assignment with the parsed results. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 323 +++++++++++++++++------------------------- 1 file changed, 131 insertions(+), 192 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c70d3469ab29..cdd8561f5f4b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -405,6 +405,7 @@ struct alc_spec { unsigned int no_analog :1; /* digital I/O only */ unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int single_input_src:1; + unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ /* auto-mute control */ int automute_mode; @@ -2677,11 +2678,15 @@ static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + unsigned long val; int err; mutex_lock(&codec->control_mutex); - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, - HDA_INPUT); + if (spec->vol_in_capsrc) + val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); + kcontrol->private_value = val; err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); mutex_unlock(&codec->control_mutex); return err; @@ -2692,11 +2697,15 @@ static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + unsigned long val; int err; mutex_lock(&codec->control_mutex); - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, - HDA_INPUT); + if (spec->vol_in_capsrc) + val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); + kcontrol->private_value = val; err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); mutex_unlock(&codec->control_mutex); return err; @@ -2725,9 +2734,14 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, } } else { i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); + if (spec->vol_in_capsrc) + kcontrol->private_value = + HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i], + 3, 0, HDA_OUTPUT); + else + kcontrol->private_value = + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], + 3, 0, HDA_INPUT); err = func(kcontrol, ucontrol); } error: @@ -5381,10 +5395,14 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) return (pincap & AC_PINCAP_IN) != 0; } -static int alc_auto_fill_adc_caps(struct hda_codec *codec, hda_nid_t *adc_nids, - hda_nid_t *cap_nids, int max_nums) +static int alc_auto_fill_adc_caps(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; hda_nid_t nid; + hda_nid_t *adc_nids = spec->private_adc_nids; + hda_nid_t *cap_nids = spec->private_capsrc_nids; + int max_nums = ARRAY_SIZE(spec->private_adc_nids); + bool indep_capsrc = false; int i, nums = 0; nid = codec->start_nid; @@ -5406,11 +5424,13 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec, hda_nid_t *adc_nids, break; if (type == AC_WID_AUD_SEL) { cap_nids[nums] = src; + indep_capsrc = true; break; } n = snd_hda_get_conn_list(codec, src, &list); if (n > 1) { cap_nids[nums] = src; + indep_capsrc = true; break; } else if (n != 1) break; @@ -5419,6 +5439,10 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec, hda_nid_t *adc_nids, if (++nums >= max_nums) break; } + spec->adc_nids = spec->private_adc_nids; + if (indep_capsrc) + spec->capsrc_nids = spec->private_capsrc_nids; + spec->num_adc_nids = nums; return nums; } @@ -5430,11 +5454,10 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) hda_nid_t mixer = spec->mixer_nid; struct hda_input_mux *imux = &spec->private_imux[0]; int num_adcs; - hda_nid_t caps[5], adcs[5]; int i, c, err, idx, type_idx = 0; const char *prev_label = NULL; - num_adcs = alc_auto_fill_adc_caps(codec, adcs, caps, ARRAY_SIZE(adcs)); + num_adcs = alc_auto_fill_adc_caps(codec); if (num_adcs < 0) return 0; @@ -5465,7 +5488,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) } for (c = 0; c < num_adcs; c++) { - idx = get_connection_index(codec, caps[c], pin); + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[c] : spec->adc_nids[c]; + idx = get_connection_index(codec, cap, pin); if (idx >= 0) { snd_hda_add_imux_item(imux, label, idx, NULL); break; @@ -5552,6 +5577,7 @@ static void alc880_auto_init_input_src(struct hda_codec *codec) static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, int (*fill_dac)(struct hda_codec *)); +static void alc_remove_invalid_adc_nids(struct hda_codec *codec); /* parse the BIOS configuration and set up the alc_spec */ /* return 1 if successful, 0 if the proper config is not found, @@ -5599,6 +5625,9 @@ static int alc880_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); return 1; @@ -5742,9 +5771,21 @@ static void set_capture_mixer(struct hda_codec *codec) alc_capture_mixer2, alc_capture_mixer3 }, }; - if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { + + /* check whether either of ADC or MUX has a volume control */ + if (!(query_amp_caps(codec, spec->adc_nids[0], HDA_INPUT) & + AC_AMPCAP_NUM_STEPS)) { + if (!spec->capsrc_nids) + return; /* no volume */ + if (!(query_amp_caps(codec, spec->capsrc_nids[0], HDA_OUTPUT) & + AC_AMPCAP_NUM_STEPS)) + return; /* no volume in capsrc, too */ + spec->vol_in_capsrc = 1; + } + + if (spec->num_adc_nids > 0) { int mux = 0; - int num_adcs = spec->num_adc_nids; + int num_adcs = 0; if (spec->dual_adc_switch) num_adcs = 1; else if (spec->auto_mic) @@ -5755,70 +5796,52 @@ static void set_capture_mixer(struct hda_codec *codec) else if (spec->input_mux->num_items == 1) fixup_single_adc(codec); } + if (!num_adcs) { + if (spec->num_adc_nids > 3) + spec->num_adc_nids = 3; + else if (!spec->num_adc_nids) + return; + num_adcs = spec->num_adc_nids; + } spec->cap_mixer = caps[mux][num_adcs - 1]; } } -/* fill adc_nids (and capsrc_nids) containing all active input pins */ -static void fillup_priv_adc_nids(struct hda_codec *codec, const hda_nid_t *nids, - int num_nids) +/* filter out invalid adc_nids (and capsrc_nids) that don't give all + * active input pins + */ +static void alc_remove_invalid_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int n; - hda_nid_t fallback_adc = 0, fallback_cap = 0; - - for (n = 0; n < num_nids; n++) { - hda_nid_t adc, cap; - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int nconns, i, j; + hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + int i, n, nums; - adc = nids[n]; - if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN) - continue; - cap = adc; - nconns = snd_hda_get_connections(codec, cap, conn, - ARRAY_SIZE(conn)); - if (nconns == 1) { - cap = conn[0]; - nconns = snd_hda_get_connections(codec, cap, conn, - ARRAY_SIZE(conn)); - } - if (nconns <= 0) - continue; - if (!fallback_adc) { - fallback_adc = adc; - fallback_cap = cap; - } + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + hda_nid_t cap = spec->private_capsrc_nids[n]; for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - for (j = 0; j < nconns; j++) { - if (conn[j] == nid) - break; - } - if (j >= nconns) + hda_nid_t pin = cfg->inputs[i].pin; + if (get_connection_index(codec, cap, pin) < 0) break; } if (i >= cfg->num_inputs) { - int num_adcs = spec->num_adc_nids; - spec->private_adc_nids[num_adcs] = adc; - spec->private_capsrc_nids[num_adcs] = cap; - spec->num_adc_nids++; - spec->adc_nids = spec->private_adc_nids; - if (adc != cap) - spec->capsrc_nids = spec->private_capsrc_nids; + adc_nids[nums] = spec->private_adc_nids[n]; + capsrc_nids[nums++] = cap; } } - if (!spec->num_adc_nids) { + if (!nums) { printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" " using fallback 0x%x\n", - codec->chip_name, fallback_adc); - spec->private_adc_nids[0] = fallback_adc; - spec->adc_nids = spec->private_adc_nids; - if (fallback_adc != fallback_cap) { - spec->private_capsrc_nids[0] = fallback_cap; - spec->capsrc_nids = spec->private_adc_nids; - } + codec->chip_name, spec->private_adc_nids[0]); + spec->num_adc_nids = 1; + } else if (nums != spec->num_adc_nids) { + memcpy(spec->private_adc_nids, adc_nids, + nums * sizeof(hda_nid_t)); + memcpy(spec->private_capsrc_nids, capsrc_nids, + nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; } } @@ -5907,17 +5930,8 @@ static int patch_alc880(struct hda_codec *codec) spec->stream_digital_capture = &alc880_pcm_digital_capture; if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]); - /* get type */ - wcap = get_wcaps_type(wcap); - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc880_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); - } else { - spec->adc_nids = alc880_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); - } + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -7173,6 +7187,9 @@ static int alc260_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); return 1; @@ -7463,17 +7480,8 @@ static int patch_alc260(struct hda_codec *codec) spec->stream_digital_capture = &alc260_pcm_digital_capture; if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x04 is valid */ - unsigned int wcap = get_wcaps(codec, 0x04); - wcap = get_wcaps_type(wcap); - /* get type */ - if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { - spec->adc_nids = alc260_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); - } else { - spec->adc_nids = alc260_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); - } + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } set_capture_mixer(codec); set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); @@ -10947,6 +10955,9 @@ static int alc882_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); err = alc_auto_add_mic_boost(codec); @@ -11048,34 +11059,8 @@ static int patch_alc882(struct hda_codec *codec) spec->stream_digital_capture = &alc882_pcm_digital_capture; if (!spec->adc_nids && spec->input_mux) { - int i, j; - spec->num_adc_nids = 0; - for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) { - const struct hda_input_mux *imux = spec->input_mux; - hda_nid_t cap; - hda_nid_t nid = alc882_adc_nids[i]; - unsigned int wcap = get_wcaps(codec, nid); - /* get type */ - wcap = get_wcaps_type(wcap); - if (wcap != AC_WID_AUD_IN) - continue; - spec->private_adc_nids[spec->num_adc_nids] = nid; - err = snd_hda_get_connections(codec, nid, &cap, 1); - if (err < 0) - continue; - err = snd_hda_get_conn_list(codec, cap, NULL); - if (err < 0) - continue; - for (j = 0; j < imux->num_items; j++) - if (imux->items[j].index >= err) - break; - if (j < imux->num_items) - continue; - spec->private_capsrc_nids[spec->num_adc_nids] = cap; - spec->num_adc_nids++; - } - spec->adc_nids = spec->private_adc_nids; - spec->capsrc_nids = spec->private_capsrc_nids; + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } set_capture_mixer(codec); @@ -12308,6 +12293,9 @@ static int alc262_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -12727,36 +12715,8 @@ static int patch_alc262(struct hda_codec *codec) spec->stream_digital_capture = &alc262_pcm_digital_capture; if (!spec->adc_nids && spec->input_mux) { - int i; - /* check whether the digital-mic has to be supported */ - for (i = 0; i < spec->input_mux->num_items; i++) { - if (spec->input_mux->items[i].index >= 9) - break; - } - if (i < spec->input_mux->num_items) { - /* use only ADC0 */ - spec->adc_nids = alc262_dmic_adc_nids; - spec->num_adc_nids = 1; - spec->capsrc_nids = alc262_dmic_capsrc_nids; - } else { - /* all analog inputs */ - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); - - /* get type */ - wcap = get_wcaps_type(wcap); - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc262_adc_nids_alt; - spec->num_adc_nids = - ARRAY_SIZE(alc262_adc_nids_alt); - spec->capsrc_nids = alc262_capsrc_nids_alt; - } else { - spec->adc_nids = alc262_adc_nids; - spec->num_adc_nids = - ARRAY_SIZE(alc262_adc_nids); - spec->capsrc_nids = alc262_capsrc_nids; - } - } + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } if (!spec->cap_mixer && !spec->no_analog) set_capture_mixer(codec); @@ -13462,6 +13422,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 2; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -13773,29 +13736,13 @@ static int patch_alc268(struct hda_codec *codec) } if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); - - spec->capsrc_nids = alc268_capsrc_nids; - /* get type */ - wcap = get_wcaps_type(wcap); - if (spec->auto_mic || - wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { - spec->adc_nids = alc268_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); - if (spec->auto_mic) - fixup_automic_adc(codec); - if (spec->auto_mic || spec->input_mux->num_items == 1) - add_mixer(spec, alc268_capture_nosrc_mixer); - else - add_mixer(spec, alc268_capture_alt_mixer); - } else { - spec->adc_nids = alc268_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); - add_mixer(spec, alc268_capture_mixer); - } + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } + if (!spec->cap_mixer && !spec->no_analog) + set_capture_mixer(codec); + spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; @@ -13833,10 +13780,6 @@ static const hda_nid_t alc269vb_capsrc_nids[1] = { 0x22, }; -static const hda_nid_t alc269_adc_candidates[] = { - 0x08, 0x09, 0x07, 0x11, -}; - #define alc269_modes alc260_modes #define alc269_capture_source alc880_lg_lw_capture_source @@ -14422,9 +14365,9 @@ static int alc269_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - if (!alc275_setup_dual_adc(codec)) - fillup_priv_adc_nids(codec, alc269_adc_candidates, - sizeof(alc269_adc_candidates)); + alc275_setup_dual_adc(codec); + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); err = alc_auto_add_mic_boost(codec); if (err < 0) @@ -14961,15 +14904,8 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_capture = &alc269_pcm_digital_capture; if (!spec->adc_nids) { /* wasn't filled automatically? use default */ - if (spec->codec_variant == ALC269_TYPE_ALC269VA) { - spec->adc_nids = alc269_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); - spec->capsrc_nids = alc269_capsrc_nids; - } else { - spec->adc_nids = alc269vb_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); - spec->capsrc_nids = alc269vb_capsrc_nids; - } + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } if (!spec->cap_mixer) @@ -15776,12 +15712,13 @@ static int alc861_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - spec->adc_nids = alc861_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); - set_capture_mixer(codec); + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); + set_capture_mixer(codec); + return 1; } @@ -16837,6 +16774,9 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -16944,11 +16884,9 @@ static int patch_alc861vd(struct hda_codec *codec) spec->stream_digital_capture = &alc861vd_pcm_digital_capture; if (!spec->adc_nids) { - spec->adc_nids = alc861vd_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } - if (!spec->capsrc_nids) - spec->capsrc_nids = alc861vd_capsrc_nids; set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -18965,6 +18903,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + if (!spec->dual_adc_switch) + alc_remove_invalid_adc_nids(codec); + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -19134,11 +19075,9 @@ static int patch_alc662(struct hda_codec *codec) spec->stream_digital_capture = &alc662_pcm_digital_capture; if (!spec->adc_nids) { - spec->adc_nids = alc662_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } - if (!spec->capsrc_nids) - spec->capsrc_nids = alc662_capsrc_nids; if (!spec->cap_mixer) set_capture_mixer(codec); @@ -19631,8 +19570,8 @@ static int patch_alc680(struct hda_codec *codec) spec->stream_digital_capture = &alc680_pcm_digital_capture; if (!spec->adc_nids) { - spec->adc_nids = alc680_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids); + alc_auto_fill_adc_caps(codec); + alc_remove_invalid_adc_nids(codec); } if (!spec->cap_mixer) -- cgit v1.2.3 From f970de2555636c563935cdc2abc5684da2adacc4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 17:39:59 +0200 Subject: ALSA: hda - Unify alc*_auto_init_input_src() in patch_realtek.c The only different implmentation was alc880_auto_init_input_src(), and now it covers this variant, and we can use the single function for all codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 62 ++++++++++++------------------------------- 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cdd8561f5f4b..b960020956c8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5553,31 +5553,10 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) } } -static void alc880_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int c; - - for (c = 0; c < spec->num_adc_nids; c++) { - unsigned int mux_idx; - const struct hda_input_mux *imux; - mux_idx = c >= spec->num_mux_defs ? 0 : c; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - if (imux) - snd_hda_codec_write(codec, spec->adc_nids[c], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); - snd_hda_codec_write(codec, spec->adc_nids[c], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - } -} - static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, int (*fill_dac)(struct hda_codec *)); static void alc_remove_invalid_adc_nids(struct hda_codec *codec); +static void alc_auto_init_input_src(struct hda_codec *codec); /* parse the BIOS configuration and set up the alc_spec */ /* return 1 if successful, 0 if the proper config is not found, @@ -5640,7 +5619,7 @@ static void alc880_auto_init(struct hda_codec *codec) alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); - alc880_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -7156,8 +7135,6 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); } -#define alc260_auto_init_input_src alc880_auto_init_input_src - static int alc260_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -7201,7 +7178,7 @@ static void alc260_auto_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc260_auto_init_multi_out(codec); alc_auto_init_analog_input(codec); - alc260_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -10817,18 +10794,23 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { /* * BIOS auto configuration */ -static void alc882_auto_init_input_src(struct hda_codec *codec) +static void alc_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int c; + if (spec->dual_adc_switch) + return; + for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t nid = spec->capsrc_nids[c]; + hda_nid_t nid; unsigned int mux_idx; const struct hda_input_mux *imux; int conns, mute, idx, item; unsigned int wid_type; + nid = spec->capsrc_nids ? + spec->capsrc_nids[c] : spec->adc_nids[c]; /* mute ADC */ if (query_amp_caps(codec, spec->adc_nids[c], HDA_INPUT) & AC_AMPCAP_MUTE) @@ -10974,7 +10956,7 @@ static void alc882_auto_init(struct hda_codec *codec) alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); - alc882_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -12305,8 +12287,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc262_auto_init_input_src alc882_auto_init_input_src - /* init callback for auto-configuration model -- overriding the default init */ static void alc262_auto_init(struct hda_codec *codec) @@ -12315,7 +12295,7 @@ static void alc262_auto_init(struct hda_codec *codec) alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); - alc262_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -13419,7 +13399,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) add_verb(spec, alc268_beep_init_verbs); } - spec->num_mux_defs = 2; + spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; if (!spec->dual_adc_switch) @@ -13434,8 +13414,6 @@ static int alc268_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc268_auto_init_input_src alc882_auto_init_input_src - /* init callback for auto-configuration model -- overriding the default init */ static void alc268_auto_init(struct hda_codec *codec) { @@ -13444,7 +13422,7 @@ static void alc268_auto_init(struct hda_codec *codec) alc268_auto_init_hp_out(codec); alc268_auto_init_mono_speaker_out(codec); alc_auto_init_analog_input(codec); - alc268_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -14381,7 +14359,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) #define alc269_auto_init_multi_out alc268_auto_init_multi_out #define alc269_auto_init_hp_out alc268_auto_init_hp_out -#define alc269_auto_init_input_src alc882_auto_init_input_src /* init callback for auto-configuration model -- overriding the default init */ @@ -14391,8 +14368,7 @@ static void alc269_auto_init(struct hda_codec *codec) alc269_auto_init_multi_out(codec); alc269_auto_init_hp_out(codec); alc_auto_init_analog_input(codec); - if (!spec->dual_adc_switch) - alc269_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -16604,8 +16580,6 @@ static const struct alc_config_preset alc861vd_presets[] = { /* * BIOS auto configuration */ -#define alc861vd_auto_init_input_src alc882_auto_init_input_src - #define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) #define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) @@ -16793,7 +16767,7 @@ static void alc861vd_auto_init(struct hda_codec *codec) alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); - alc861vd_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -18687,8 +18661,6 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) spec->multiout.extra_out_nid[0]); } -#define alc662_auto_init_input_src alc882_auto_init_input_src - /* * multi-io helper */ @@ -18926,7 +18898,7 @@ static void alc662_auto_init(struct hda_codec *codec) alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); - alc662_auto_init_input_src(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); -- cgit v1.2.3 From c2d986b0d21e53453de8751ecbb317148820d065 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Jul 2011 18:30:08 +0200 Subject: ALSA: hda - Clean-up PCM assignments in patch_realtek.c Instead of assigning each default hda_pcm_stream pointers, do NULL-checks and assign default values in alc_build_pcms(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 229 +++++++++++------------------------------- 1 file changed, 58 insertions(+), 171 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b960020956c8..2e7afd434525 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4203,7 +4203,7 @@ static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) /* * Analog playback callbacks */ -static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, +static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4212,7 +4212,7 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, hinfo); } -static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -4223,7 +4223,7 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stream_tag, format, substream); } -static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4234,7 +4234,7 @@ static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, /* * Digital out */ -static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4242,7 +4242,7 @@ static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_open(codec, &spec->multiout); } -static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -4253,7 +4253,7 @@ static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stream_tag, format, substream); } -static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4261,7 +4261,7 @@ static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); } -static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4272,7 +4272,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, /* * Analog capture */ -static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -4285,7 +4285,7 @@ static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, return 0; } -static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4334,57 +4334,57 @@ static const struct hda_pcm_stream dualmic_pcm_analog_capture = { /* */ -static const struct hda_pcm_stream alc880_pcm_analog_playback = { +static const struct hda_pcm_stream alc_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, /* NID is set in alc_build_pcms */ .ops = { - .open = alc880_playback_pcm_open, - .prepare = alc880_playback_pcm_prepare, - .cleanup = alc880_playback_pcm_cleanup + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup }, }; -static const struct hda_pcm_stream alc880_pcm_analog_capture = { +static const struct hda_pcm_stream alc_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ }; -static const struct hda_pcm_stream alc880_pcm_analog_alt_playback = { +static const struct hda_pcm_stream alc_pcm_analog_alt_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ }; -static const struct hda_pcm_stream alc880_pcm_analog_alt_capture = { +static const struct hda_pcm_stream alc_pcm_analog_alt_capture = { .substreams = 2, /* can be overridden */ .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ .ops = { - .prepare = alc880_alt_capture_pcm_prepare, - .cleanup = alc880_alt_capture_pcm_cleanup + .prepare = alc_alt_capture_pcm_prepare, + .cleanup = alc_alt_capture_pcm_cleanup }, }; -static const struct hda_pcm_stream alc880_pcm_digital_playback = { +static const struct hda_pcm_stream alc_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ .ops = { - .open = alc880_dig_playback_pcm_open, - .close = alc880_dig_playback_pcm_close, - .prepare = alc880_dig_playback_pcm_prepare, - .cleanup = alc880_dig_playback_pcm_cleanup + .open = alc_dig_playback_pcm_open, + .close = alc_dig_playback_pcm_close, + .prepare = alc_dig_playback_pcm_prepare, + .cleanup = alc_dig_playback_pcm_cleanup }, }; -static const struct hda_pcm_stream alc880_pcm_digital_capture = { +static const struct hda_pcm_stream alc_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -4402,6 +4402,7 @@ static int alc_build_pcms(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; + const struct hda_pcm_stream *p; int i; codec->num_pcms = 1; @@ -4414,16 +4415,18 @@ static int alc_build_pcms(struct hda_codec *codec) "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; - if (spec->stream_analog_playback) { - if (snd_BUG_ON(!spec->multiout.dac_nids)) - return -EINVAL; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); + if (spec->multiout.dac_nids > 0) { + p = spec->stream_analog_playback; + if (!p) + p = &alc_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; } - if (spec->stream_analog_capture) { - if (snd_BUG_ON(!spec->adc_nids)) - return -EINVAL; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); + if (spec->adc_nids) { + p = spec->stream_analog_capture; + if (!p) + p = &alc_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; } @@ -4450,14 +4453,18 @@ static int alc_build_pcms(struct hda_codec *codec) info->pcm_type = spec->dig_out_type; else info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid && - spec->stream_digital_playback) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); + if (spec->multiout.dig_out_nid) { + p = spec->stream_digital_playback; + if (!p) + p = &alc_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; } - if (spec->dig_in_nid && - spec->stream_digital_capture) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture); + if (spec->dig_in_nid) { + p = spec->stream_digital_capture; + if (!p) + p = &alc_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } /* FIXME: do we need this for all Realtek codec models? */ @@ -4471,14 +4478,15 @@ static int alc_build_pcms(struct hda_codec *codec) * model, configure a second analog capture-only PCM. */ /* Additional Analaog capture for index #2 */ - if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) || - (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) { + if (spec->alt_dac_nid || spec->num_adc_nids > 1) { codec->num_pcms = 3; info = spec->pcm_rec + 2; info->name = spec->stream_name_analog; if (spec->alt_dac_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_analog_alt_playback; + p = spec->stream_analog_alt_playback; + if (!p) + p = &alc_pcm_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->alt_dac_nid; } else { @@ -4486,9 +4494,11 @@ static int alc_build_pcms(struct hda_codec *codec) alc_pcm_null_stream; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; } - if (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *spec->stream_analog_alt_capture; + if (spec->num_adc_nids > 1) { + p = spec->stream_analog_alt_capture; + if (!p) + p = &alc_pcm_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1]; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = @@ -5901,13 +5911,6 @@ static int patch_alc880(struct hda_codec *codec) if (board_config != ALC880_AUTO) setup_preset(codec, &alc880_presets[board_config]); - spec->stream_analog_playback = &alc880_pcm_analog_playback; - spec->stream_analog_capture = &alc880_pcm_analog_capture; - spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; - - spec->stream_digital_playback = &alc880_pcm_digital_playback; - spec->stream_digital_capture = &alc880_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); @@ -7000,12 +7003,6 @@ static const struct hda_verb alc260_test_init_verbs[] = { }; #endif -#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback -#define alc260_pcm_analog_capture alc880_pcm_analog_capture - -#define alc260_pcm_digital_playback alc880_pcm_digital_playback -#define alc260_pcm_digital_capture alc880_pcm_digital_capture - /* * for BIOS auto-configuration */ @@ -7449,13 +7446,6 @@ static int patch_alc260(struct hda_codec *codec) if (board_config != ALC260_AUTO) setup_preset(codec, &alc260_presets[board_config]); - spec->stream_analog_playback = &alc260_pcm_analog_playback; - spec->stream_analog_capture = &alc260_pcm_analog_capture; - spec->stream_analog_alt_capture = &alc260_pcm_analog_capture; - - spec->stream_digital_playback = &alc260_pcm_digital_playback; - spec->stream_digital_capture = &alc260_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); @@ -9783,12 +9773,6 @@ static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) #define alc882_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc882_pcm_analog_playback alc880_pcm_analog_playback -#define alc882_pcm_analog_capture alc880_pcm_analog_capture -#define alc882_pcm_digital_playback alc880_pcm_digital_playback -#define alc882_pcm_digital_capture alc880_pcm_digital_capture - static const hda_nid_t alc883_slave_dig_outs[] = { ALC1200_DIGOUT_NID, 0, }; @@ -11031,15 +11015,6 @@ static int patch_alc882(struct hda_codec *codec) if (board_config != ALC882_AUTO) setup_preset(codec, &alc882_presets[board_config]); - spec->stream_analog_playback = &alc882_pcm_analog_playback; - spec->stream_analog_capture = &alc882_pcm_analog_capture; - /* FIXME: setup DAC5 */ - /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/ - spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; - - spec->stream_digital_playback = &alc882_pcm_digital_playback; - spec->stream_digital_capture = &alc882_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); @@ -12230,12 +12205,6 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { #define alc262_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc262_pcm_analog_playback alc880_pcm_analog_playback -#define alc262_pcm_analog_capture alc880_pcm_analog_capture -#define alc262_pcm_digital_playback alc880_pcm_digital_playback -#define alc262_pcm_digital_capture alc880_pcm_digital_capture - /* * BIOS auto configuration */ @@ -12688,12 +12657,6 @@ static int patch_alc262(struct hda_codec *codec) if (board_config != ALC262_AUTO) setup_preset(codec, &alc262_presets[board_config]); - spec->stream_analog_playback = &alc262_pcm_analog_playback; - spec->stream_analog_capture = &alc262_pcm_analog_capture; - - spec->stream_digital_playback = &alc262_pcm_digital_playback; - spec->stream_digital_capture = &alc262_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); @@ -13352,12 +13315,6 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); } -/* pcm configuration: identical with ALC880 */ -#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 */ @@ -13684,12 +13641,6 @@ static int patch_alc268(struct hda_codec *codec) if (board_config != ALC268_AUTO) setup_preset(codec, &alc268_presets[board_config]); - 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_digital_playback = &alc268_pcm_digital_playback; - has_beep = 0; for (i = 0; i < spec->num_mixers; i++) { if (spec->mixers[i] == alc268_beep_mixer) { @@ -14214,12 +14165,6 @@ static const struct hda_verb alc269vb_init_verbs[] = { #define alc269_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical 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 - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -14227,9 +14172,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ /* NID is set in alc_build_pcms */ .ops = { - .open = alc880_playback_pcm_open, - .prepare = alc880_playback_pcm_prepare, - .cleanup = alc880_playback_pcm_cleanup + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup }, }; @@ -14868,16 +14813,7 @@ static int patch_alc269(struct hda_codec *codec) */ 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; } - spec->stream_digital_playback = &alc269_pcm_digital_playback; - spec->stream_digital_capture = &alc269_pcm_digital_capture; if (!spec->adc_nids) { /* wasn't filled automatically? use default */ alc_auto_fill_adc_caps(codec); @@ -15440,13 +15376,6 @@ static void alc861_toshiba_unsol_event(struct hda_codec *codec, alc861_toshiba_automute(codec); } -/* pcm configuration: identical with ALC880 */ -#define alc861_pcm_analog_playback alc880_pcm_analog_playback -#define alc861_pcm_analog_capture alc880_pcm_analog_capture -#define alc861_pcm_digital_playback alc880_pcm_digital_playback -#define alc861_pcm_digital_capture alc880_pcm_digital_capture - - #define ALC861_DIGOUT_NID 0x07 static const struct hda_channel_mode alc861_8ch_modes[1] = { @@ -15940,12 +15869,6 @@ static int patch_alc861(struct hda_codec *codec) if (board_config != ALC861_AUTO) setup_preset(codec, &alc861_presets[board_config]); - spec->stream_analog_playback = &alc861_pcm_analog_playback; - spec->stream_analog_capture = &alc861_pcm_analog_capture; - - spec->stream_digital_playback = &alc861_pcm_digital_playback; - spec->stream_digital_capture = &alc861_pcm_digital_capture; - if (!spec->cap_mixer) set_capture_mixer(codec); set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); @@ -16424,12 +16347,6 @@ static void alc861vd_dallas_setup(struct hda_codec *codec) #define alc861vd_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback -#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture -#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback -#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture - /* * configuration and preset */ @@ -16851,12 +16768,6 @@ static int patch_alc861vd(struct hda_codec *codec) add_verb(spec, alc660vd_eapd_verbs); } - spec->stream_analog_playback = &alc861vd_pcm_analog_playback; - spec->stream_analog_capture = &alc861vd_pcm_analog_capture; - - spec->stream_digital_playback = &alc861vd_pcm_digital_playback; - spec->stream_digital_capture = &alc861vd_pcm_digital_capture; - if (!spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); @@ -17847,12 +17758,6 @@ static const struct snd_kcontrol_new alc272_nc10_mixer[] = { #endif -/* pcm configuration: identical with ALC880 */ -#define alc662_pcm_analog_playback alc880_pcm_analog_playback -#define alc662_pcm_analog_capture alc880_pcm_analog_capture -#define alc662_pcm_digital_playback alc880_pcm_digital_playback -#define alc662_pcm_digital_capture alc880_pcm_digital_capture - /* * configuration and preset */ @@ -19040,12 +18945,6 @@ static int patch_alc662(struct hda_codec *codec) if (board_config != ALC662_AUTO) setup_preset(codec, &alc662_presets[board_config]); - spec->stream_analog_playback = &alc662_pcm_analog_playback; - spec->stream_analog_capture = &alc662_pcm_analog_capture; - - spec->stream_digital_playback = &alc662_pcm_digital_playback; - spec->stream_digital_capture = &alc662_pcm_digital_capture; - if (!spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); @@ -19405,13 +19304,6 @@ static void alc680_auto_init_hp_out(struct hda_codec *codec) 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 -#define alc680_pcm_digital_capture alc880_pcm_digital_capture - /* * BIOS auto configuration */ @@ -19536,11 +19428,6 @@ static int patch_alc680(struct hda_codec *codec) 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_auto_capture; - spec->stream_digital_playback = &alc680_pcm_digital_playback; - spec->stream_digital_capture = &alc680_pcm_digital_capture; - if (!spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_remove_invalid_adc_nids(codec); -- cgit v1.2.3 From a926757f0431042b32ef4188ce8201cbe0fcbb50 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Jul 2011 15:12:55 +0200 Subject: ALSA: hda - Fix warning with ALC882 digital-out detection The digital out pin on ALC882 may have multiple connections. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2e7afd434525..53188c4cbf75 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2080,11 +2080,13 @@ static void alc_auto_parse_digital(struct hda_codec *codec) /* support multiple SPDIFs; the secondary is set up as a slave */ for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t conn[4]; err = snd_hda_get_connections(codec, spec->autocfg.dig_out_pins[i], - &dig_nid, 1); + conn, ARRAY_SIZE(conn)); if (err < 0) continue; + dig_nid = conn[0]; /* assume the first element is audio-out */ if (!i) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; -- cgit v1.2.3 From 21268961d3d1bbdd22a19b68adb80119e8c72dcd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Jul 2011 15:01:13 +0200 Subject: ALSA: hda - More flexible dynamic-ADC switching for Realtek codecs This patch changes the auto-parser and the auto-mic handling codes to allow more flexible dynamic ADC-switching with Realtek codecs. In the new code, the following strategy is taken: - When a cap-src can't handle all input-sources, either skip it, or switch to the ADC-switching mode. In ADC-switching mode, like the former dual-ADC mode for ALC275, it changes ADC on the fly according to the current input source. - When auto-mic is possible, always assign imux. If the mic pins are set statically via a quirk, rebuild imux according to the pins. In the auto-mic mode, the driver always changes the imux (although the imux isn't exposed as a mixer element). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 915 ++++++++++++++++++++---------------------- 1 file changed, 429 insertions(+), 486 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 53188c4cbf75..42026f4978c3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -276,14 +276,6 @@ enum { ALC_INIT_GPIO3, }; -struct alc_mic_route { - hda_nid_t pin; - unsigned char mux_idx; - unsigned char amix_idx; -}; - -#define MUX_IDX_UNDEF ((unsigned char)-1) - struct alc_customize_define { unsigned int sku_cfg; unsigned char port_connectivity; @@ -351,7 +343,6 @@ struct alc_spec { hda_nid_t mixer_nid; /* analog-mixer NID */ /* 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; @@ -360,9 +351,9 @@ struct alc_spec { unsigned int num_mux_defs; const struct hda_input_mux *input_mux; unsigned int cur_mux[3]; - struct alc_mic_route ext_mic; - struct alc_mic_route dock_mic; - struct alc_mic_route int_mic; + hda_nid_t ext_mic_pin; + hda_nid_t dock_mic_pin; + hda_nid_t int_mic_pin; /* channel model */ const struct hda_channel_mode *channel_mode; @@ -382,6 +373,9 @@ struct alc_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; + unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; + int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ /* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -396,6 +390,7 @@ struct alc_spec { unsigned int line_jack_present:1; unsigned int master_mute:1; unsigned int auto_mic:1; + unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ unsigned int automute:1; /* HP automute enabled */ unsigned int detect_line:1; /* Line-out detection enabled */ unsigned int automute_lines:1; /* automute line-out as well */ @@ -403,7 +398,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ - unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int single_input_src:1; unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ @@ -495,47 +490,81 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, return 0; } -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; + + 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, 1); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + return true; + } + return false; +} + +/* select the given imux item; either unmute exclusively or select the route */ +static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx, bool force) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int mux_idx; - hda_nid_t nid = spec->capsrc_nids ? - spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; - unsigned int type; + int i, type; + hda_nid_t nid; mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; imux = &spec->input_mux[mux_idx]; if (!imux->num_items && mux_idx > 0) imux = &spec->input_mux[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[adc_idx] == idx && !force) + return 0; + spec->cur_mux[adc_idx] = idx; + + if (spec->dyn_adc_switch) { + alc_dyn_adc_pcm_resetup(codec, idx); + adc_idx = spec->dyn_adc_idx[idx]; + } + + nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; + + /* no selection? */ + if (snd_hda_get_conn_list(codec, nid, NULL) <= 1) + return 1; + type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_AUD_MIX) { /* Matrix-mixer style (e.g. ALC882) */ - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; for (i = 0; i < imux->num_items; i++) { unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, imux->items[i].index, HDA_AMP_MUTE, v); } - *cur_val = idx; - return 1; } else { /* MUX style (e.g. ALC880) */ - return snd_hda_input_mux_put(codec, imux, ucontrol, nid, - &spec->cur_mux[adc_idx]); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); } + return 1; +} + +static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return alc_mux_select(codec, adc_idx, + ucontrol->value.enumerated.item[0], false); } /* @@ -1059,8 +1088,8 @@ static int alc_init_jacks(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err; unsigned int hp_nid = spec->autocfg.hp_pins[0]; - unsigned int mic_nid = spec->ext_mic.pin; - unsigned int dock_nid = spec->dock_mic.pin; + unsigned int mic_nid = spec->ext_mic_pin; + unsigned int dock_nid = spec->dock_mic_pin; if (hp_nid) { err = snd_hda_input_jack_add(codec, hp_nid, @@ -1199,93 +1228,29 @@ static void alc_line_automute(struct hda_codec *codec) #define get_connection_index(codec, mux, nid) \ snd_hda_get_conn_index(codec, mux, nid, 0) -/* 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, 1); - 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; - struct alc_mic_route *dead1, *dead2, *alive; - unsigned int present, type; - hda_nid_t cap_nid; + hda_nid_t *pins = spec->imux_pins; - if (!spec->auto_mic) - return; - if (!spec->int_mic.pin || !spec->ext_mic.pin) + if (!spec->auto_mic || !spec->auto_mic_valid_imux) return; if (snd_BUG_ON(!spec->adc_nids)) return; - - if (spec->dual_adc_switch) { - alc_dual_mic_adc_auto_switch(codec); + if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) return; - } - - cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; - - alive = &spec->int_mic; - dead1 = &spec->ext_mic; - dead2 = &spec->dock_mic; - - present = snd_hda_jack_detect(codec, spec->ext_mic.pin); - if (present) { - alive = &spec->ext_mic; - dead1 = &spec->int_mic; - dead2 = &spec->dock_mic; - } - if (!present && spec->dock_mic.pin > 0) { - present = snd_hda_jack_detect(codec, spec->dock_mic.pin); - if (present) { - alive = &spec->dock_mic; - dead1 = &spec->int_mic; - dead2 = &spec->ext_mic; - } - snd_hda_input_jack_report(codec, spec->dock_mic.pin); - } - type = get_wcaps_type(get_wcaps(codec, cap_nid)); - if (type == AC_WID_AUD_MIX) { - /* Matrix-mixer style (e.g. ALC882) */ - snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, - alive->mux_idx, - HDA_AMP_MUTE, 0); - if (dead1->pin > 0) - snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, - dead1->mux_idx, - HDA_AMP_MUTE, HDA_AMP_MUTE); - if (dead2->pin > 0) - snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, - dead2->mux_idx, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* MUX style (e.g. ALC880) */ - snd_hda_codec_write_cache(codec, cap_nid, 0, - AC_VERB_SET_CONNECT_SEL, - alive->mux_idx); - } - snd_hda_input_jack_report(codec, spec->ext_mic.pin); + if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx])) + alc_mux_select(codec, 0, spec->ext_mic_idx, false); + else if (spec->dock_mic_idx >= 0 && + snd_hda_jack_detect(codec, pins[spec->dock_mic_idx])) + alc_mux_select(codec, 0, spec->dock_mic_idx, false); + else + alc_mux_select(codec, 0, spec->int_mic_idx, false); - /* FIXME: analog mixer */ + snd_hda_input_jack_report(codec, pins[spec->ext_mic_idx]); + if (spec->dock_mic_idx >= 0) + snd_hda_input_jack_report(codec, pins[spec->dock_mic_idx]); } /* unsolicited event for HP jack sensing */ @@ -1602,6 +1567,87 @@ static void alc_init_auto_hp(struct hda_codec *codec) } } +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +static bool alc_check_dyn_adc_switch(struct hda_codec *codec); + +/* rebuild imux for matching with the given auto-mic pins (if not yet) */ +static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux; + static char * const texts[3] = { + "Mic", "Internal Mic", "Dock Mic" + }; + int i; + + if (!spec->auto_mic) + return false; + imux = &spec->private_imux[0]; + if (spec->input_mux == imux) + return true; + spec->imux_pins[0] = spec->ext_mic_pin; + spec->imux_pins[1] = spec->int_mic_pin; + spec->imux_pins[2] = spec->dock_mic_pin; + for (i = 0; i < 3; i++) { + strcpy(imux->items[i].label, texts[i]); + if (spec->imux_pins[i]) + imux->num_items = i + 1; + } + spec->num_mux_defs = 1; + spec->input_mux = imux; + return true; +} + +/* check whether all auto-mic pins are valid; setup indices if OK */ +static bool alc_auto_mic_check_imux(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; + + if (!spec->auto_mic) + return false; + if (spec->auto_mic_valid_imux) + return true; /* already checked */ + + /* fill up imux indices */ + if (!alc_check_dyn_adc_switch(codec)) { + spec->auto_mic = 0; + return false; + } + + imux = spec->input_mux; + spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, + spec->imux_pins, imux->num_items); + spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, + spec->imux_pins, imux->num_items); + spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, + spec->imux_pins, imux->num_items); + if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) { + spec->auto_mic = 0; + return false; /* no corresponding imux */ + } + + snd_hda_codec_write_cache(codec, spec->ext_mic_pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_MIC_EVENT); + if (spec->dock_mic_pin) + snd_hda_codec_write_cache(codec, spec->dock_mic_pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_MIC_EVENT); + + spec->auto_mic_valid_imux = 1; + spec->auto_mic = 1; + return true; +} + static void alc_init_auto_mic(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1609,6 +1655,8 @@ static void alc_init_auto_mic(struct hda_codec *codec) hda_nid_t fixed, ext, dock; int i; + spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; + fixed = ext = dock = 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; @@ -1650,18 +1698,18 @@ static void alc_init_auto_mic(struct hda_codec *codec) return; /* no unsol support */ if (dock && !is_jack_detectable(codec, dock)) return; /* no unsol support */ + + /* check imux indices */ + spec->ext_mic_pin = ext; + spec->int_mic_pin = fixed; + spec->dock_mic_pin = dock; + + spec->auto_mic = 1; + if (!alc_auto_mic_check_imux(codec)) + return; + snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", ext, fixed, dock); - spec->ext_mic.pin = ext; - spec->dock_mic.pin = dock; - spec->int_mic.pin = fixed; - spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ - spec->dock_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ - spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ - spec->auto_mic = 1; - snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_MIC_EVENT); spec->unsol_event = alc_sku_unsol_event; } @@ -1737,11 +1785,7 @@ do_sku: static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return true; - return false; + return find_idx_in_nid_list(nid, list, nums) >= 0; } /* check subsystem ID and set up device-specific initialization; @@ -1871,7 +1915,11 @@ static void alc_ssid_check(struct hda_codec *codec, "Enable default setup for auto mode as fallback\n"); spec->init_amp = ALC_INIT_DEFAULT; } +} +/* check the availabilities of auto-mute and auto-mic switches */ +static void alc_auto_check_switches(struct hda_codec *codec) +{ alc_init_auto_hp(codec); alc_init_auto_mic(codec); } @@ -2722,10 +2770,10 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int i, err; + int i, err = 0; mutex_lock(&codec->control_mutex); - if (check_adc_switch && spec->dual_adc_switch) { + if (check_adc_switch && spec->dyn_adc_switch) { for (i = 0; i < spec->num_adc_nids; i++) { kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], @@ -2742,8 +2790,8 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, 3, 0, HDA_OUTPUT); else kcontrol->private_value = - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], + 3, 0, HDA_INPUT); err = func(kcontrol, ucontrol); } error: @@ -4299,21 +4347,21 @@ static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, } /* analog capture with dynamic dual-adc changes */ -static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo, +static int dyn_adc_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 = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; 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, +static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4323,14 +4371,14 @@ static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } -static const struct hda_pcm_stream dualmic_pcm_analog_capture = { +static const struct hda_pcm_stream dyn_adc_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 + .prepare = dyn_adc_capture_pcm_prepare, + .cleanup = dyn_adc_capture_pcm_cleanup }, }; @@ -4426,8 +4474,12 @@ static int alc_build_pcms(struct hda_codec *codec) } if (spec->adc_nids) { p = spec->stream_analog_capture; - if (!p) - p = &alc_pcm_analog_capture; + if (!p) { + if (spec->dyn_adc_switch) + p = &dyn_adc_pcm_analog_capture; + else + p = &alc_pcm_analog_capture; + } info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; } @@ -5452,8 +5504,7 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec) break; } spec->adc_nids = spec->private_adc_nids; - if (indep_capsrc) - spec->capsrc_nids = spec->private_capsrc_nids; + spec->capsrc_nids = spec->private_capsrc_nids; spec->num_adc_nids = nums; return nums; } @@ -5504,11 +5555,16 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) spec->capsrc_nids[c] : spec->adc_nids[c]; idx = get_connection_index(codec, cap, pin); if (idx >= 0) { + spec->imux_pins[imux->num_items] = pin; snd_hda_add_imux_item(imux, label, idx, NULL); break; } } } + + spec->num_mux_defs = 1; + spec->input_mux = imux; + return 0; } @@ -5613,13 +5669,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); + alc_remove_invalid_adc_nids(codec); alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); return 1; } @@ -5637,45 +5690,6 @@ static void alc880_auto_init(struct hda_codec *codec) alc_inithook(codec); } -/* check the ADC/MUX contains all input pins; some ADC/MUX contains only - * one of two digital mic pins, e.g. on ALC272 - */ -static void fixup_automic_adc(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = spec->capsrc_nids ? - spec->capsrc_nids[i] : spec->adc_nids[i]; - int iidx, eidx; - - iidx = get_connection_index(codec, cap, spec->int_mic.pin); - if (iidx < 0) - continue; - eidx = get_connection_index(codec, cap, spec->ext_mic.pin); - if (eidx < 0) - continue; - spec->int_mic.mux_idx = iidx; - spec->ext_mic.mux_idx = eidx; - if (spec->capsrc_nids) - spec->capsrc_nids += i; - spec->adc_nids += i; - spec->num_adc_nids = 1; - /* optional dock-mic */ - eidx = get_connection_index(codec, cap, spec->dock_mic.pin); - if (eidx < 0) - spec->dock_mic.pin = 0; - else - spec->dock_mic.mux_idx = eidx; - return; - } - snd_printd(KERN_INFO "hda_codec: %s: " - "No ADC/MUX containing both 0x%x and 0x%x pins\n", - codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin); - spec->auto_mic = 0; /* disable auto-mic to be sure */ -} - /* select or unmute the given capsrc route */ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, int idx) @@ -5683,7 +5697,7 @@ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, HDA_AMP_MUTE, 0); - } else { + } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) { snd_hda_codec_write_cache(codec, cap, 0, AC_VERB_SET_CONNECT_SEL, idx); } @@ -5711,44 +5725,14 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) 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; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - /* search for the input pin; there must be only one */ - if (cfg->num_inputs != 1) - return; - i = init_capsrc_for_pin(codec, cfg->inputs[0].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; - spec->single_input_src = 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->dock_mic.pin); - init_capsrc_for_pin(codec, spec->int_mic.pin); -} - /* initialize some special cases for input sources */ static void alc_init_special_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (spec->dual_adc_switch) - fixup_dual_adc_switch(codec); - else if (spec->single_input_src) - init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin); + int i; + + for (i = 0; i < spec->autocfg.num_inputs; i++) + init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); } static void set_capture_mixer(struct hda_codec *codec) @@ -5777,16 +5761,14 @@ static void set_capture_mixer(struct hda_codec *codec) if (spec->num_adc_nids > 0) { int mux = 0; int num_adcs = 0; - if (spec->dual_adc_switch) + + if (spec->input_mux && spec->input_mux->num_items > 1) + mux = 1; + if (spec->auto_mic) { + num_adcs = 1; + mux = 0; + } else if (spec->dyn_adc_switch) num_adcs = 1; - else if (spec->auto_mic) - fixup_automic_adc(codec); - else if (spec->input_mux) { - if (spec->input_mux->num_items > 1) - mux = 1; - else if (spec->input_mux->num_items == 1) - fixup_single_adc(codec); - } if (!num_adcs) { if (spec->num_adc_nids > 3) spec->num_adc_nids = 3; @@ -5798,35 +5780,92 @@ static void set_capture_mixer(struct hda_codec *codec) } } +/* check whether dynamic ADC-switching is available */ +static bool alc_check_dyn_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, n, idx; + hda_nid_t cap, pin; + + if (imux != spec->input_mux) /* no dynamic imux? */ + return false; + + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!pin) + return false; + if (get_connection_index(codec, cap, pin) < 0) + break; + } + if (i >= imux->num_items) + return false; /* no ADC-switch is needed */ + } + + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + idx = get_connection_index(codec, cap, pin); + if (idx >= 0) { + imux->items[i].index = idx; + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("realtek: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + return true; +} + /* filter out invalid adc_nids (and capsrc_nids) that don't give all * active input pins */ static void alc_remove_invalid_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; + const struct hda_input_mux *imux; hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; int i, n, nums; + imux = spec->input_mux; + if (!imux) + return; + if (spec->dyn_adc_switch) + return; + nums = 0; for (n = 0; n < spec->num_adc_nids; n++) { hda_nid_t cap = spec->private_capsrc_nids[n]; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - if (get_connection_index(codec, cap, pin) < 0) + int num_conns = snd_hda_get_conn_list(codec, cap, NULL); + for (i = 0; i < imux->num_items; i++) { + hda_nid_t pin = spec->imux_pins[i]; + if (pin) { + if (get_connection_index(codec, cap, pin) < 0) + break; + } else if (num_conns <= imux->items[i].index) break; } - if (i >= cfg->num_inputs) { + if (i >= imux->num_items) { adc_nids[nums] = spec->private_adc_nids[n]; capsrc_nids[nums++] = cap; } } if (!nums) { - printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", - codec->chip_name, spec->private_adc_nids[0]); - spec->num_adc_nids = 1; + /* check whether ADC-switch is possible */ + if (!alc_check_dyn_adc_switch(codec)) { + printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" + " using fallback 0x%x\n", + codec->chip_name, spec->private_adc_nids[0]); + spec->num_adc_nids = 1; + spec->auto_mic = 0; + return; + } } else if (nums != spec->num_adc_nids) { memcpy(spec->private_adc_nids, adc_nids, nums * sizeof(hda_nid_t)); @@ -5834,6 +5873,11 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec) nums * sizeof(hda_nid_t)); spec->num_adc_nids = nums; } + + if (spec->auto_mic) + alc_auto_mic_check_imux(codec); /* check auto-mic setups */ + else if (spec->input_mux->num_items == 1) + spec->num_adc_nids = 1; /* reduce to a single ADC */ } #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -5915,6 +5959,7 @@ static int patch_alc880(struct hda_codec *codec) if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } set_capture_mixer(codec); @@ -7160,13 +7205,10 @@ static int alc260_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); + alc_remove_invalid_adc_nids(codec); alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); + alc_auto_check_switches(codec); return 1; } @@ -7450,6 +7492,7 @@ static int patch_alc260(struct hda_codec *codec) if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } set_capture_mixer(codec); @@ -9704,10 +9747,8 @@ static void alc883_mode2_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x15; spec->autocfg.speaker_pins[2] = 0x16; - spec->ext_mic.pin = 0x18; - spec->int_mic.pin = 0x19; - spec->ext_mic.mux_idx = 0; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_AMP; @@ -10780,67 +10821,41 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { /* * BIOS auto configuration */ -static void alc_auto_init_input_src(struct hda_codec *codec) +static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) { struct alc_spec *spec = codec->spec; - int c; - - if (spec->dual_adc_switch) - return; + hda_nid_t nid; - for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t nid; - unsigned int mux_idx; - const struct hda_input_mux *imux; - int conns, mute, idx, item; - unsigned int wid_type; - - nid = spec->capsrc_nids ? - spec->capsrc_nids[c] : spec->adc_nids[c]; - /* mute ADC */ - if (query_amp_caps(codec, spec->adc_nids[c], HDA_INPUT) & - AC_AMPCAP_MUTE) - snd_hda_codec_write(codec, spec->adc_nids[c], 0, + nid = spec->adc_nids[adc_idx]; + /* mute ADC */ + if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); - else if (query_amp_caps(codec, nid, HDA_OUTPUT) & - AC_AMPCAP_MUTE) - snd_hda_codec_write(codec, nid, 0, + return; + } + if (!spec->capsrc_nids) + return; + nid = spec->capsrc_nids[adc_idx]; + if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); +} - conns = snd_hda_get_conn_list(codec, nid, NULL); - if (conns <= 0) - continue; - mux_idx = c >= spec->num_mux_defs ? 0 : c; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - wid_type = get_wcaps_type(get_wcaps(codec, nid)); - for (idx = 0; idx < conns; idx++) { - /* if the current connection is the selected one, - * unmute it as default - otherwise mute it - */ - mute = AMP_IN_MUTE(idx); - for (item = 0; item < imux->num_items; item++) { - if (imux->items[item].index == idx) { - if (spec->cur_mux[c] == item) - mute = AMP_IN_UNMUTE(idx); - break; - } - } - /* initialize the mute status if mute-amp is present */ - if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - mute); - if (wid_type == AC_WID_AUD_SEL && - mute != AMP_IN_MUTE(idx)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - idx); - } - } +static void alc_auto_init_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int c, nums; + + for (c = 0; c < spec->num_adc_nids; c++) + alc_auto_init_adc(codec, c); + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + for (c = 0; c < nums; c++) + alc_mux_select(codec, 0, spec->cur_mux[c], true); } /* add mic boosts if needed */ @@ -10920,18 +10935,15 @@ static int alc882_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - err = alc_auto_add_mic_boost(codec); if (err < 0) return err; + alc_remove_invalid_adc_nids(codec); + + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); + return 1; /* config found */ } @@ -11019,6 +11031,7 @@ static int patch_alc882(struct hda_codec *codec) if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } @@ -11515,10 +11528,8 @@ static void alc262_toshiba_s06_setup(struct hda_codec *codec) spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; @@ -12243,17 +12254,14 @@ static int alc262_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); - err = alc_auto_add_mic_boost(codec); if (err < 0) return err; + alc_remove_invalid_adc_nids(codec); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); return 1; } @@ -12661,6 +12669,7 @@ static int patch_alc262(struct hda_codec *codec) if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } if (!spec->cap_mixer && !spec->no_analog) @@ -12863,10 +12872,8 @@ static void alc268_acer_lc_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_AMP; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 6; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; } @@ -12896,10 +12903,8 @@ static void alc268_dell_setup(struct hda_codec *codec) spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; @@ -12928,10 +12933,8 @@ static void alc267_quanta_il1_setup(struct hda_codec *codec) struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; @@ -13358,17 +13361,14 @@ static int alc268_parse_auto_config(struct hda_codec *codec) add_verb(spec, alc268_beep_init_verbs); } - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); - err = alc_auto_add_mic_boost(codec); if (err < 0) return err; + alc_remove_invalid_adc_nids(codec); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); return 1; } @@ -13668,6 +13668,7 @@ static int patch_alc268(struct hda_codec *codec) if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } @@ -13924,10 +13925,8 @@ static void alc269_quanta_fl1_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -14019,10 +14018,8 @@ static void alc269_laptop_amic_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -14034,10 +14031,8 @@ static void alc269_laptop_dmic_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 5; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; } @@ -14049,10 +14044,8 @@ static void alc269vb_laptop_amic_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -14064,10 +14057,8 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 6; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; } @@ -14217,36 +14208,6 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) } #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; -} - /* different alc269-variants */ enum { ALC269_TYPE_ALC269VA, @@ -14282,17 +14243,13 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); + alc_remove_invalid_adc_nids(codec); + if (spec->codec_variant != ALC269_TYPE_ALC269VA) alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); else alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - alc275_setup_dual_adc(codec); - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); + alc_auto_check_switches(codec); err = alc_auto_add_mic_boost(codec); if (err < 0) @@ -14819,6 +14776,7 @@ static int patch_alc269(struct hda_codec *codec) if (!spec->adc_nids) { /* wasn't filled automatically? use default */ alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } @@ -15616,13 +15574,10 @@ static int alc861_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); + alc_remove_invalid_adc_nids(codec); alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); + alc_auto_check_switches(codec); set_capture_mixer(codec); @@ -15871,6 +15826,12 @@ static int patch_alc861(struct hda_codec *codec) if (board_config != ALC861_AUTO) setup_preset(codec, &alc861_presets[board_config]); + if (!spec->adc_nids) { + alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); + alc_remove_invalid_adc_nids(codec); + } + if (!spec->cap_mixer) set_capture_mixer(codec); set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); @@ -16664,18 +16625,15 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; + alc_remove_invalid_adc_nids(codec); - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); err = alc_auto_add_mic_boost(codec); if (err < 0) return err; - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - return 1; } @@ -16772,6 +16730,7 @@ static int patch_alc861vd(struct hda_codec *codec) if (!spec->adc_nids) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } @@ -17539,10 +17498,8 @@ static void alc662_eeepc_setup(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc262_hippo1_setup(codec); - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17564,10 +17521,8 @@ static void alc663_m51va_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; } @@ -17580,10 +17535,8 @@ static void alc663_mode1_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17595,10 +17548,8 @@ static void alc662_mode2_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17611,10 +17562,8 @@ static void alc663_mode3_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17629,10 +17578,8 @@ static void alc663_mode4_setup(struct hda_codec *codec) spec->automute_mixer_nid[1] = 0x0e; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17647,10 +17594,8 @@ static void alc663_mode5_setup(struct hda_codec *codec) spec->automute_mixer_nid[1] = 0x0e; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17664,10 +17609,8 @@ static void alc663_mode6_setup(struct hda_codec *codec) spec->automute_mixer_nid[0] = 0x0c; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17681,10 +17624,8 @@ static void alc663_mode7_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x17; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; spec->auto_mic = 1; } @@ -17698,10 +17639,8 @@ static void alc663_mode8_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x17; spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; } @@ -17715,10 +17654,8 @@ static void alc663_g71v_setup(struct hda_codec *codec) spec->automute_mode = ALC_AUTOMUTE_AMP; spec->detect_line = 1; spec->automute_lines = 1; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; spec->auto_mic = 1; } @@ -18779,21 +18716,18 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!spec->dual_adc_switch) - alc_remove_invalid_adc_nids(codec); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; + alc_remove_invalid_adc_nids(codec); if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670) alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21); else alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; return 1; } @@ -18949,6 +18883,7 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->adc_nids) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } @@ -19036,36 +18971,32 @@ static const hda_nid_t alc680_adc_nids[3] = { /* * Analog capture ADC cgange */ -static void alc680_rec_autoswitch(struct hda_codec *codec) +static hda_nid_t alc680_get_cur_adc(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int pin_found = 0; - int type_found = AUTO_PIN_LAST; - hda_nid_t nid; + static hda_nid_t pins[] = {0x18, 0x19}; + static hda_nid_t adcs[] = {0x08, 0x09}; int i; - for (i = 0; i < cfg->num_inputs; i++) { - nid = cfg->inputs[i].pin; - if (!is_jack_detectable(codec, nid)) + for (i = 0; i < ARRAY_SIZE(pins); i++) { + if (!is_jack_detectable(codec, pins[i])) continue; - if (snd_hda_jack_detect(codec, nid)) { - if (cfg->inputs[i].type < type_found) { - type_found = cfg->inputs[i].type; - pin_found = nid; - } - } + if (snd_hda_jack_detect(codec, pins[i])) + return adcs[i]; } + return 0x07; +} - nid = 0x07; - if (pin_found) - snd_hda_get_connections(codec, pin_found, &nid, 1); - - if (nid != spec->cur_adc) +static void alc680_rec_autoswitch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = alc680_get_cur_adc(codec); + if (spec->cur_adc && nid != spec->cur_adc) { __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = nid; - snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); + spec->cur_adc = nid; + snd_hda_codec_setup_stream(codec, nid, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } } static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -19075,12 +19006,12 @@ static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; + hda_nid_t nid = alc680_get_cur_adc(codec); - spec->cur_adc = 0x07; + spec->cur_adc = nid; spec->cur_adc_stream_tag = stream_tag; spec->cur_adc_format = format; - - alc680_rec_autoswitch(codec); + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); return 0; } @@ -19088,9 +19019,9 @@ static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_cleanup_stream(codec, 0x07); - snd_hda_codec_cleanup_stream(codec, 0x08); - snd_hda_codec_cleanup_stream(codec, 0x09); + struct alc_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; return 0; } @@ -19332,6 +19263,10 @@ static int alc680_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + err = alc_auto_create_input_ctls(codec); + if (err < 0) + return err; + spec->multiout.max_channels = 2; dig_only: @@ -19340,6 +19275,10 @@ static int alc680_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); + alc_remove_invalid_adc_nids(codec); + + alc_auto_check_switches(codec); + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -19354,6 +19293,7 @@ static void alc680_auto_init(struct hda_codec *codec) alc680_auto_init_multi_out(codec); alc680_auto_init_hp_out(codec); alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -19427,11 +19367,14 @@ static int patch_alc680(struct hda_codec *codec) } } - if (board_config != ALC680_AUTO) + if (board_config != ALC680_AUTO) { setup_preset(codec, &alc680_presets[board_config]); + spec->stream_analog_capture = &alc680_pcm_analog_auto_capture; + } if (!spec->adc_nids) { alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } -- cgit v1.2.3 From 0e4a73ae5893d61ae10a9f219e2f3371e44589a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Jul 2011 17:05:48 +0200 Subject: ALSA: hda - Use common paser for digital I/O for ALC260 Avoid open-codes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 42026f4978c3..8366e02df3cc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7200,8 +7200,8 @@ static int alc260_parse_auto_config(struct hda_codec *codec) spec->multiout.max_channels = 2; - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = ALC260_DIGOUT_NID; + alc_auto_parse_digital(codec); + if (spec->kctls.list) add_mixer(spec, spec->kctls.list); -- cgit v1.2.3 From 1d045db96ad9b8f4d876d5945ab097425252e4ab Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Jul 2011 18:23:21 +0200 Subject: ALSA: hda - Split quirk codes from patch_realtek.c Put the all static quirk codes out of patch_realtek.c, split into the file for each codec model. For controlling the build of quirk codes, a new Kconfig, CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS is introduced. By setting this off, all quirk codes won't be built, thus you can save lots of memory. The codes in patch_realtek.c are also shuffled and more comments are given, but the contents aren't changed. This is just a refactoring. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 13 + sound/pci/hda/alc260_quirks.c | 1272 +++ sound/pci/hda/alc262_quirks.c | 1353 +++ sound/pci/hda/alc268_quirks.c | 636 ++ sound/pci/hda/alc269_quirks.c | 681 ++ sound/pci/hda/alc662_quirks.c | 1408 +++ sound/pci/hda/alc680_quirks.c | 222 + sound/pci/hda/alc861_quirks.c | 725 ++ sound/pci/hda/alc861vd_quirks.c | 605 ++ sound/pci/hda/alc880_quirks.c | 1898 ++++ sound/pci/hda/alc882_quirks.c | 3755 ++++++++ sound/pci/hda/alc_quirks.c | 467 + sound/pci/hda/patch_realtek.c | 18518 ++++++-------------------------------- 13 files changed, 15878 insertions(+), 15675 deletions(-) create mode 100644 sound/pci/hda/alc260_quirks.c create mode 100644 sound/pci/hda/alc262_quirks.c create mode 100644 sound/pci/hda/alc268_quirks.c create mode 100644 sound/pci/hda/alc269_quirks.c create mode 100644 sound/pci/hda/alc662_quirks.c create mode 100644 sound/pci/hda/alc680_quirks.c create mode 100644 sound/pci/hda/alc861_quirks.c create mode 100644 sound/pci/hda/alc861vd_quirks.c create mode 100644 sound/pci/hda/alc880_quirks.c create mode 100644 sound/pci/hda/alc882_quirks.c create mode 100644 sound/pci/hda/alc_quirks.c diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 85217bd96d85..70762fca57ee 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -83,6 +83,19 @@ config SND_HDA_CODEC_REALTEK snd-hda-codec-realtek. This module is automatically loaded at probing. +config SND_HDA_ENABLE_REALTEK_QUIRKS + bool "Build static quirks for Realtek codecs" + depends on SND_HDA_CODEC_REALTEK + default y + help + Say Y here to build the static quirks codes for Realtek codecs. + If you need the "model" preset that the default BIOS auto-parser + can't handle, turn this option on. + + If your device works with model=auto option, basically you don't + need the quirk code. By turning this off, you can reduce the + module size quite a lot. + config SND_HDA_CODEC_ANALOG bool "Build Analog Device HD-audio codec support" default y diff --git a/sound/pci/hda/alc260_quirks.c b/sound/pci/hda/alc260_quirks.c new file mode 100644 index 000000000000..21ec2cb100b0 --- /dev/null +++ b/sound/pci/hda/alc260_quirks.c @@ -0,0 +1,1272 @@ +/* + * ALC260 quirk models + * included by patch_realtek.c + */ + +/* ALC260 models */ +enum { + ALC260_AUTO, + ALC260_BASIC, + ALC260_HP, + ALC260_HP_DC7600, + ALC260_HP_3013, + ALC260_FUJITSU_S702X, + ALC260_ACER, + ALC260_WILL, + ALC260_REPLACER_672V, + ALC260_FAVORIT100, +#ifdef CONFIG_SND_DEBUG + ALC260_TEST, +#endif + ALC260_MODEL_LAST /* last tag */ +}; + +static const hda_nid_t alc260_dac_nids[1] = { + /* front */ + 0x02, +}; + +static const hda_nid_t alc260_adc_nids[1] = { + /* ADC0 */ + 0x04, +}; + +static const hda_nid_t alc260_adc_nids_alt[1] = { + /* ADC1 */ + 0x05, +}; + +/* NIDs used when simultaneous access to both ADCs makes sense. Note that + * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. + */ +static const hda_nid_t alc260_dual_adc_nids[2] = { + /* ADC0, ADC1 */ + 0x04, 0x05 +}; + +#define ALC260_DIGOUT_NID 0x03 +#define ALC260_DIGIN_NID 0x06 + +static const struct hda_input_mux alc260_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack, + * headphone jack and the internal CD lines since these are the only pins at + * which audio can appear. For flexibility, also allow the option of + * recording the mixer output on the second ADC (ADC0 doesn't have a + * connection to the mixer output). + */ +static const struct hda_input_mux alc260_fujitsu_capture_sources[2] = { + { + .num_items = 3, + .items = { + { "Mic/Line", 0x0 }, + { "CD", 0x4 }, + { "Headphone", 0x2 }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic/Line", 0x0 }, + { "CD", 0x4 }, + { "Headphone", 0x2 }, + { "Mixer", 0x5 }, + }, + }, + +}; + +/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to + * the Fujitsu S702x, but jacks are marked differently. + */ +static const struct hda_input_mux alc260_acer_capture_sources[2] = { + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Headphone", 0x5 }, + }, + }, + { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Headphone", 0x6 }, + { "Mixer", 0x5 }, + }, + }, +}; + +/* Maxdata Favorit 100XS */ +static const struct hda_input_mux alc260_favorit100_capture_sources[2] = { + { + .num_items = 2, + .items = { + { "Line/Mic", 0x0 }, + { "CD", 0x4 }, + }, + }, + { + .num_items = 3, + .items = { + { "Line/Mic", 0x0 }, + { "CD", 0x4 }, + { "Mixer", 0x5 }, + }, + }, +}; + +/* + * This is just place-holder, so there's something for alc_build_pcms to look + * at when it calculates the maximum number of channels. ALC260 has no mixer + * element which allows changing the channel mode, so the verb list is + * never used. + */ +static const struct hda_channel_mode alc260_modes[1] = { + { 2, NULL }, +}; + + +/* Mixer combinations + * + * basic: base_output + input + pc_beep + capture + * HP: base_output + input + capture_alt + * HP_3013: hp_3013 + input + capture + * fujitsu: fujitsu + capture + * acer: acer + capture + */ + +static const struct snd_kcontrol_new alc260_base_output_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc260_input_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT), + { } /* end */ +}; + +/* update HP, line and mono out pins according to the master switch */ +static void alc260_hp_master_update(struct hda_codec *codec) +{ + update_speakers(codec); +} + +static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + *ucontrol->value.integer.value = !spec->master_mute; + return 0; +} + +static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int val = !*ucontrol->value.integer.value; + + if (val == spec->master_mute) + return 0; + spec->master_mute = val; + alc260_hp_master_update(codec); + return 1; +} + +static const struct snd_kcontrol_new alc260_hp_output_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + }, + HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc260_hp_unsol_verbs[] = { + {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {}, +}; + +static void alc260_hp_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x0f; + spec->autocfg.speaker_pins[0] = 0x10; + spec->autocfg.speaker_pins[1] = 0x11; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct snd_kcontrol_new alc260_hp_3013_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + }, + HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static void alc260_hp_3013_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x10; + spec->autocfg.speaker_pins[1] = 0x11; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct hda_bind_ctls alc260_dc7600_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc260_dc7600_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol), + HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct hda_verb alc260_hp_3013_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {}, +}; + +static void alc260_hp_3012_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x10; + spec->autocfg.speaker_pins[0] = 0x0f; + spec->autocfg.speaker_pins[1] = 0x11; + spec->autocfg.speaker_pins[2] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, + * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. + */ +static const struct snd_kcontrol_new alc260_fujitsu_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT), + ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT), + ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT), + { } /* end */ +}; + +/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current + * versions of the ALC260 don't act on requests to enable mic bias from NID + * 0x0f (used to drive the headphone jack in these laptops). The ALC260 + * datasheet doesn't mention this restriction. At this stage it's not clear + * whether this behaviour is intentional or is a hardware bug in chip + * revisions available in early 2006. Therefore for now allow the + * "Headphone Jack Mode" control to span all choices, but if it turns out + * that the lack of mic bias for this NID is intentional we could change the + * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. + * + * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006 + * don't appear to make the mic bias available from the "line" jack, even + * though the NID used for this jack (0x14) can supply it. The theory is + * that perhaps Acer have included blocking capacitors between the ALC260 + * and the output jack. If this turns out to be the case for all such + * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT + * to ALC_PIN_DIR_INOUT_NOMICBIAS. + * + * The C20x Tablet series have a mono internal speaker which is controlled + * via the chip's Mono sum widget and pin complex, so include the necessary + * controls for such models. On models without a "mono speaker" the control + * won't do anything. + */ +static const struct snd_kcontrol_new alc260_acer_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), + ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, + HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), + { } /* end */ +}; + +/* Maxdata Favorit 100XS: one output and one input (0x12) jack + */ +static const struct snd_kcontrol_new alc260_favorit100_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), + ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), + HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), + { } /* end */ +}; + +/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12, + * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17. + */ +static const struct snd_kcontrol_new alc260_will_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + { } /* end */ +}; + +/* Replacer 672V ALC260 pin usage: Mic jack = 0x12, + * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f. + */ +static const struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), + ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), + { } /* end */ +}; + +/* + * initialization verbs + */ +static const struct hda_verb alc260_init_verbs[] = { + /* Line In pin widget for input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* CD pin widget for input */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /* LINE-2 is used for line-out in rear */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* select line-out */ + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* LINE-OUT pin */ + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* enable HP */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* enable Mono */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* mute capture amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* set connection select to line in (default select for this ADC) */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* mute capture amp left and right */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* set connection select to line in (default select for this ADC) */ + {0x05, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* set vol=0 Line-Out mixer amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* unmute pin widget amp left and right (no gain on this amp) */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* set vol=0 HP mixer amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* unmute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* set vol=0 Mono mixer amp left and right */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* unmute pin widget amp left and right (no gain on this amp) */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* unmute LINE-2 out pin */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & + * Line In 2 = 0x03 + */ + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ + /* mute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* mute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* mute Mono out path */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } +}; + +#if 0 /* should be identical with alc260_init_verbs? */ +static const struct hda_verb alc260_hp_init_verbs[] = { + /* Headphone and output */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + /* mono output */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Line In pin widget for input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Line-2 pin widget for output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* CD pin widget for input */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* unmute amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute Line-Out mixer amp left and right (volume = 0) */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute HP mixer amp left and right (volume = 0) */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & + * Line In 2 = 0x03 + */ + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ + /* Unmute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Mono out path */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + { } +}; +#endif + +static const struct hda_verb alc260_hp_3013_init_verbs[] = { + /* Line out and output */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* mono output */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Line In pin widget for input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Headphone pin widget for output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + /* CD pin widget for input */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* unmute amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute Line-Out mixer amp left and right (volume = 0) */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute HP mixer amp left and right (volume = 0) */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & + * Line In 2 = 0x03 + */ + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ + /* Unmute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Mono out path */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + { } +}; + +/* Initialisation sequence for ALC260 as configured in Fujitsu S702x + * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD + * audio = 0x16, internal speaker = 0x10. + */ +static const struct hda_verb alc260_fujitsu_init_verbs[] = { + /* Disable all GPIOs */ + {0x01, AC_VERB_SET_GPIO_MASK, 0}, + /* Internal speaker is connected to headphone pin */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Headphone/Line-out jack connects to Line1 pin; make it an output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Mic/Line-in jack is connected to mic1 pin, so make it an input */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Ensure all other unused pins are disabled and muted. */ + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + + /* Disable digital (SPDIF) pins */ + {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, + {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, + + /* Ensure Line1 pin widget takes its input from the OUT1 sum bus + * when acting as an output. + */ + {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Start with output sum widgets muted and their output gains at min */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute Line1 pin widget output buffer since it starts as an output. + * If the pin mode is changed by the user the pin mode control will + * take care of enabling the pin's input/output buffers as needed. + * Therefore there's no need to enable the input buffer at this + * stage. + */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute input buffer of pin widget used for Line-in (no equiv + * mixer ctrl) + */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute capture amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* Set ADC connection select to match default mixer setting - line + * in (on mic1 pin) + */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Do the same for the second ADC: mute capture input amp and + * set ADC connection to line in (on mic1 pin) + */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mute all inputs to mixer widget (even unconnected ones) */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ + + { } +}; + +/* Initialisation sequence for ALC260 as configured in Acer TravelMate and + * similar laptops (adapted from Fujitsu init verbs). + */ +static const struct hda_verb alc260_acer_init_verbs[] = { + /* On TravelMate laptops, GPIO 0 enables the internal speaker and + * the headphone jack. Turn this on and rely on the standard mute + * methods whenever the user wants to turn these outputs off. + */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, + /* Internal speaker/Headphone jack is connected to Line-out pin */ + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Internal microphone/Mic jack is connected to Mic1 pin */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + /* Line In jack is connected to Line1 pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Ensure all other unused pins are disabled and muted. */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* Disable digital (SPDIF) pins */ + {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, + {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, + + /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum + * bus when acting as outputs. + */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Start with output sum widgets muted and their output gains at min */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Unmute Line-out pin widget amp left and right + * (no equiv mixer ctrl) + */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute mono pin widget amp output (no equiv mixer ctrl) */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute Mic1 and Line1 pin widget input buffers since they start as + * inputs. If the pin mode is changed by the user the pin mode control + * will take care of enabling the pin's input/output buffers as needed. + * Therefore there's no need to enable the input buffer at this + * stage. + */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute capture amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* Set ADC connection select to match default mixer setting - mic + * (on mic1 pin) + */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Do similar with the second ADC: mute capture input amp and + * set ADC connection to mic to match ALSA's default state. + */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mute all inputs to mixer widget (even unconnected ones) */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ + + { } +}; + +/* Initialisation sequence for Maxdata Favorit 100XS + * (adapted from Acer init verbs). + */ +static const struct hda_verb alc260_favorit100_init_verbs[] = { + /* GPIO 0 enables the output jack. + * Turn this on and rely on the standard mute + * methods whenever the user wants to turn these outputs off. + */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, + /* Line/Mic input jack is connected to Mic1 pin */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + /* Ensure all other unused pins are disabled and muted. */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* Disable digital (SPDIF) pins */ + {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, + {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, + + /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum + * bus when acting as outputs. + */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Start with output sum widgets muted and their output gains at min */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Unmute Line-out pin widget amp left and right + * (no equiv mixer ctrl) + */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute Mic1 and Line1 pin widget input buffers since they start as + * inputs. If the pin mode is changed by the user the pin mode control + * will take care of enabling the pin's input/output buffers as needed. + * Therefore there's no need to enable the input buffer at this + * stage. + */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute capture amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* Set ADC connection select to match default mixer setting - mic + * (on mic1 pin) + */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Do similar with the second ADC: mute capture input amp and + * set ADC connection to mic to match ALSA's default state. + */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mute all inputs to mixer widget (even unconnected ones) */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ + + { } +}; + +static const struct hda_verb alc260_will_verbs[] = { + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x1a, AC_VERB_SET_PROC_COEF, 0x3040}, + {} +}; + +static const struct hda_verb alc260_replacer_672v_verbs[] = { + {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x1a, AC_VERB_SET_PROC_COEF, 0x3050}, + + {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + + {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc260_replacer_672v_automute(struct hda_codec *codec) +{ + unsigned int present; + + /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ + present = snd_hda_jack_detect(codec, 0x0f); + if (present) { + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 1); + snd_hda_codec_write_cache(codec, 0x0f, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_HP); + } else { + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0); + snd_hda_codec_write_cache(codec, 0x0f, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + } +} + +static void alc260_replacer_672v_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc260_replacer_672v_automute(codec); +} + +static const struct hda_verb alc260_hp_dc7600_verbs[] = { + {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +/* Test configuration for debugging, modelled after the ALC880 test + * configuration. + */ +#ifdef CONFIG_SND_DEBUG +static const hda_nid_t alc260_test_dac_nids[1] = { + 0x02, +}; +static const hda_nid_t alc260_test_adc_nids[2] = { + 0x04, 0x05, +}; +/* For testing the ALC260, each input MUX needs its own definition since + * the signal assignments are different. This assumes that the first ADC + * is NID 0x04. + */ +static const struct hda_input_mux alc260_test_capture_sources[2] = { + { + .num_items = 7, + .items = { + { "MIC1 pin", 0x0 }, + { "MIC2 pin", 0x1 }, + { "LINE1 pin", 0x2 }, + { "LINE2 pin", 0x3 }, + { "CD pin", 0x4 }, + { "LINE-OUT pin", 0x5 }, + { "HP-OUT pin", 0x6 }, + }, + }, + { + .num_items = 8, + .items = { + { "MIC1 pin", 0x0 }, + { "MIC2 pin", 0x1 }, + { "LINE1 pin", 0x2 }, + { "LINE2 pin", 0x3 }, + { "CD pin", 0x4 }, + { "Mixer", 0x5 }, + { "LINE-OUT pin", 0x6 }, + { "HP-OUT pin", 0x7 }, + }, + }, +}; +static const struct snd_kcontrol_new alc260_test_mixer[] = { + /* Output driver widgets */ + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), + HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT), + HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT), + + /* Modes for retasking pin widgets + * Note: the ALC260 doesn't seem to act on requests to enable mic + * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't + * mention this restriction. At this stage it's not clear whether + * this behaviour is intentional or is a hardware bug in chip + * revisions available at least up until early 2006. Therefore for + * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all + * choices, but if it turns out that the lack of mic bias for these + * NIDs is intentional we could change their modes from + * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. + */ + ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT), + + /* Loopback mixer controls */ + HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT), + HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT), + + /* Controls for GPIO pins, assuming they are configured as outputs */ + ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), + ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), + ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), + ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), + + /* Switches to allow the digital IO pins to be enabled. The datasheet + * is ambigious as to which NID is which; testing on laptops which + * make this output available should provide clarification. + */ + ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01), + ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), + + /* A switch allowing EAPD to be enabled. Some laptops seem to use + * this output to turn on an external amplifier. + */ + ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), + ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), + + { } /* end */ +}; +static const struct hda_verb alc260_test_init_verbs[] = { + /* Enable all GPIOs as outputs with an initial value of 0 */ + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + {0x01, AC_VERB_SET_GPIO_MASK, 0x0f}, + + /* Enable retasking pins as output, initially without power amp */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* Disable digital (SPDIF) pins initially, but users can enable + * them via a mixer switch. In the case of SPDIF-out, this initverb + * payload also sets the generation to 0, output to be in "consumer" + * PCM format, copyright asserted, no pre-emphasis and no validity + * control. + */ + {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, + {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, + + /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the + * OUT1 sum bus when acting as an output. + */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, + {0x0c, AC_VERB_SET_CONNECT_SEL, 0}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Start with output sum widgets muted and their output gains at min */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Unmute retasking pin widget output buffers since the default + * state appears to be output. As the pin mode is changed by the + * user the pin mode control will take care of enabling the pin's + * input/output buffers as needed. + */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Also unmute the mono-out pin widget */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Mute capture amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* Set ADC connection select to match default mixer setting (mic1 + * pin) + */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Do the same for the second ADC: mute capture input amp and + * set ADC connection to mic1 pin + */ + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mute all inputs to mixer widget (even unconnected ones) */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ + + { } +}; +#endif + +/* + * ALC260 configurations + */ +static const char * const alc260_models[ALC260_MODEL_LAST] = { + [ALC260_BASIC] = "basic", + [ALC260_HP] = "hp", + [ALC260_HP_3013] = "hp-3013", + [ALC260_HP_DC7600] = "hp-dc7600", + [ALC260_FUJITSU_S702X] = "fujitsu", + [ALC260_ACER] = "acer", + [ALC260_WILL] = "will", + [ALC260_REPLACER_672V] = "replacer", + [ALC260_FAVORIT100] = "favorit100", +#ifdef CONFIG_SND_DEBUG + [ALC260_TEST] = "test", +#endif + [ALC260_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc260_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), + SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL), + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), + SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100), + SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */ + SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600), + SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP), + SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC), + SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC), + SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), + SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), + SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), + SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), + {} +}; + +static const struct alc_config_preset alc260_presets[] = { + [ALC260_BASIC] = { + .mixers = { alc260_base_output_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), + .adc_nids = alc260_dual_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + }, + [ALC260_HP] = { + .mixers = { alc260_hp_output_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_init_verbs, + alc260_hp_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc260_hp_setup, + .init_hook = alc_inithook, + }, + [ALC260_HP_DC7600] = { + .mixers = { alc260_hp_dc7600_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_init_verbs, + alc260_hp_dc7600_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc260_hp_3012_setup, + .init_hook = alc_inithook, + }, + [ALC260_HP_3013] = { + .mixers = { alc260_hp_3013_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_hp_3013_init_verbs, + alc260_hp_3013_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc260_hp_3013_setup, + .init_hook = alc_inithook, + }, + [ALC260_FUJITSU_S702X] = { + .mixers = { alc260_fujitsu_mixer }, + .init_verbs = { alc260_fujitsu_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), + .adc_nids = alc260_dual_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources), + .input_mux = alc260_fujitsu_capture_sources, + }, + [ALC260_ACER] = { + .mixers = { alc260_acer_mixer }, + .init_verbs = { alc260_acer_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), + .adc_nids = alc260_dual_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), + .input_mux = alc260_acer_capture_sources, + }, + [ALC260_FAVORIT100] = { + .mixers = { alc260_favorit100_mixer }, + .init_verbs = { alc260_favorit100_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), + .adc_nids = alc260_dual_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources), + .input_mux = alc260_favorit100_capture_sources, + }, + [ALC260_WILL] = { + .mixers = { alc260_will_mixer }, + .init_verbs = { alc260_init_verbs, alc260_will_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), + .adc_nids = alc260_adc_nids, + .dig_out_nid = ALC260_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + }, + [ALC260_REPLACER_672V] = { + .mixers = { alc260_replacer_672v_mixer }, + .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), + .adc_nids = alc260_adc_nids, + .dig_out_nid = ALC260_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc260_replacer_672v_unsol_event, + .init_hook = alc260_replacer_672v_automute, + }, +#ifdef CONFIG_SND_DEBUG + [ALC260_TEST] = { + .mixers = { alc260_test_mixer }, + .init_verbs = { alc260_test_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), + .dac_nids = alc260_test_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids), + .adc_nids = alc260_test_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources), + .input_mux = alc260_test_capture_sources, + }, +#endif +}; + diff --git a/sound/pci/hda/alc262_quirks.c b/sound/pci/hda/alc262_quirks.c new file mode 100644 index 000000000000..8d2097d77642 --- /dev/null +++ b/sound/pci/hda/alc262_quirks.c @@ -0,0 +1,1353 @@ +/* + * ALC262 quirk models + * included by patch_realtek.c + */ + +/* ALC262 models */ +enum { + ALC262_AUTO, + ALC262_BASIC, + ALC262_HIPPO, + ALC262_HIPPO_1, + ALC262_FUJITSU, + ALC262_HP_BPC, + ALC262_HP_BPC_D7000_WL, + ALC262_HP_BPC_D7000_WF, + ALC262_HP_TC_T5735, + ALC262_HP_RP5700, + ALC262_BENQ_ED8, + ALC262_SONY_ASSAMD, + ALC262_BENQ_T31, + ALC262_ULTRA, + ALC262_LENOVO_3000, + ALC262_NEC, + ALC262_TOSHIBA_S06, + ALC262_TOSHIBA_RX1, + ALC262_TYAN, + ALC262_MODEL_LAST /* last tag */ +}; + +#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID +#define ALC262_DIGIN_NID ALC880_DIGIN_NID + +#define alc262_dac_nids alc260_dac_nids +#define alc262_adc_nids alc882_adc_nids +#define alc262_adc_nids_alt alc882_adc_nids_alt +#define alc262_capsrc_nids alc882_capsrc_nids +#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt + +#define alc262_modes alc260_modes +#define alc262_capture_source alc882_capture_source + +static const hda_nid_t alc262_dmic_adc_nids[1] = { + /* ADC0 */ + 0x09 +}; + +static const hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 }; + +static const struct snd_kcontrol_new alc262_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +/* update HP, line and mono-out pins according to the master switch */ +#define alc262_hp_master_update alc260_hp_master_update + +static void alc262_hp_bpc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static void alc262_hp_wildwest_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +#define alc262_hp_master_sw_get alc260_hp_master_sw_get +#define alc262_hp_master_sw_put alc260_hp_master_sw_put + +#define ALC262_HP_MASTER_SWITCH \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Master Playback Switch", \ + .info = snd_ctl_boolean_mono_info, \ + .get = alc262_hp_master_sw_get, \ + .put = alc262_hp_master_sw_put, \ + }, \ + { \ + .iface = NID_MAPPING, \ + .name = "Master Playback Switch", \ + .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \ + } + + +static const struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + ALC262_HP_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), + 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { + ALC262_HP_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x1a, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { + HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Rear Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hp_t5735_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + 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 Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_hp_t5735_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static const struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_hp_rp5700_verbs[] = { + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {} +}; + +static const struct hda_input_mux alc262_hp_rp5700_capture_source = { + .num_items = 1, + .items = { + { "Line", 0x1 }, + }, +}; + +/* bind hp and internal speaker mute (with plug check) as master switch */ +#define alc262_hippo_master_update alc262_hp_master_update +#define alc262_hippo_master_sw_get alc262_hp_master_sw_get +#define alc262_hippo_master_sw_put alc262_hp_master_sw_put + +#define ALC262_HIPPO_MASTER_SWITCH \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Master Playback Switch", \ + .info = snd_ctl_boolean_mono_info, \ + .get = alc262_hippo_master_sw_get, \ + .put = alc262_hippo_master_sw_put, \ + }, \ + { \ + .iface = NID_MAPPING, \ + .name = "Master Playback Switch", \ + .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \ + (SUBDEV_SPEAKER(0) << 16), \ + } + +static const struct snd_kcontrol_new alc262_hippo_mixer[] = { + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_hippo1_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hippo_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc262_hippo1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + + +static const struct snd_kcontrol_new alc262_sony_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_benq_t31_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_tyan_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT), + 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_tyan_verbs[] = { + /* Headphone automute */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* P11 AUX_IN, white 4-pin connector */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1}, + {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93}, + {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19}, + + {} +}; + +/* unsolicited event for HP jack sensing */ +static void alc262_tyan_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + + +#define alc262_capture_mixer alc882_capture_mixer +#define alc262_capture_alt_mixer alc882_capture_alt_mixer + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc262_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute input amps (CD, 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 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, 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, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + + { } +}; + +static const struct hda_verb alc262_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc262_hippo1_unsol_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc262_sony_unsol_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_toshiba_s06_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x09}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static void alc262_toshiba_s06_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +/* + * nec model + * 0x15 = headphone + * 0x16 = internal speaker + * 0x18 = external mic + */ + +static const struct snd_kcontrol_new alc262_nec_mixer[] = { + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT), + + 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 Volume", 0x18, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_nec_verbs[] = { + /* Unmute Speaker */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Headphone */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* External mic to headphone */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* External mic to speaker */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {} +}; + +/* + * fujitsu model + * 0x14 = headphone/spdif-out, 0x15 = internal speaker, + * 0x1b = port replicator headphone out + */ + +static const struct hda_verb alc262_fujitsu_unsol_verbs[] = { + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc262_lenovo_3000_init_verbs[] = { + /* Front Mic pin: input vref at 50% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {} +}; + +static const struct hda_input_mux alc262_fujitsu_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc262_HP_capture_source = { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "AUX IN", 0x6 }, + }, +}; + +static const struct hda_input_mux alc262_HP_D7000_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x2 }, + { "Line", 0x1 }, + { "CD", 0x4 }, + }, +}; + +static void alc262_fujitsu_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.hp_pins[1] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* bind volumes of both NID 0x0c and 0x0d */ +static const struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc262_fujitsu_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, + { + .iface = NID_MAPPING, + .name = "Master Playback Switch", + .private_value = 0x1b, + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static void alc262_lenovo_3000_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + ALC262_HIPPO_MASTER_SWITCH, + 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* additional init verbs for Benq laptops */ +static const struct hda_verb alc262_EAPD_verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + {} +}; + +static const struct hda_verb alc262_benq_t31_EAPD_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, + {} +}; + +/* Samsung Q1 Ultra Vista model setup */ +static const struct snd_kcontrol_new alc262_ultra_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Mic Boost Volume", 0x15, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_ultra_verbs[] = { + /* output mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* speaker */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + /* internal mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* ADC, choose mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, + {} +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_ultra_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + mute = 0; + /* auto-mute only when HP is used as HP */ + if (!spec->cur_mux[0]) { + spec->jack_present = snd_hda_jack_detect(codec, 0x15); + if (spec->jack_present) + mute = HDA_AMP_MUTE; + } + /* mute/unmute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + /* mute/unmute HP */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); +} + +/* unsolicited event for HP jack sensing */ +static void alc262_ultra_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC_HP_EVENT) + return; + alc262_ultra_automute(codec); +} + +static const struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Headphone", 0x7 }, + }, +}; + +static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int ret; + + ret = alc_mux_enum_put(kcontrol, ucontrol); + if (!ret) + return 0; + /* reprogram the HP pin as mic or HP according to the input source */ + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); + alc262_ultra_automute(codec); /* mute/unmute HP */ + return ret; +} + +static const struct snd_kcontrol_new alc262_ultra_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, + .name = "Capture Source", + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc262_ultra_mux_enum_put, + }, + { + .iface = NID_MAPPING, + .name = "Capture Source", + .private_value = 0x15, + }, + { } /* end */ +}; + +static const struct hda_verb alc262_HP_BPC_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute input amps (CD, 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)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, 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)}, + + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */ + /* Input mixer1: only unmute Mic */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, + + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } +}; + +static const struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute input amps (CD, 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)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, 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)}, + + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */ + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/ + /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } +}; + +static const struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +/* + * configuration and preset + */ +static const char * const alc262_models[ALC262_MODEL_LAST] = { + [ALC262_BASIC] = "basic", + [ALC262_HIPPO] = "hippo", + [ALC262_HIPPO_1] = "hippo_1", + [ALC262_FUJITSU] = "fujitsu", + [ALC262_HP_BPC] = "hp-bpc", + [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", + [ALC262_HP_TC_T5735] = "hp-tc-t5735", + [ALC262_HP_RP5700] = "hp-rp5700", + [ALC262_BENQ_ED8] = "benq", + [ALC262_BENQ_T31] = "benq-t31", + [ALC262_SONY_ASSAMD] = "sony-assamd", + [ALC262_TOSHIBA_S06] = "toshiba-s06", + [ALC262_TOSHIBA_RX1] = "toshiba-rx1", + [ALC262_ULTRA] = "ultra", + [ALC262_LENOVO_3000] = "lenovo-3000", + [ALC262_NEC] = "nec", + [ALC262_TYAN] = "tyan", + [ALC262_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc262_cfg_tbl[] = { + SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), + SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series", + ALC262_HP_BPC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series", + ALC262_HP_BPC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series", + ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", + ALC262_AUTO), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series", + ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", + ALC262_HP_TC_T5735), + SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), + SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), + SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */ + SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06), + SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO), + SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO), +#if 0 /* disable the quirk since model=auto works better in recent versions */ + SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", + ALC262_SONY_ASSAMD), +#endif + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", + ALC262_TOSHIBA_RX1), + SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), + SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), + SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN), + SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1", + ALC262_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), + SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), + SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), + SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), + {} +}; + +static const struct alc_config_preset alc262_presets[] = { + [ALC262_BASIC] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_HIPPO] = { + .mixers = { alc262_hippo_mixer }, + .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_HIPPO_1] = { + .mixers = { alc262_hippo1_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo1_setup, + .init_hook = alc_inithook, + }, + [ALC262_FUJITSU] = { + .mixers = { alc262_fujitsu_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_fujitsu_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_fujitsu_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_BPC] = { + .mixers = { alc262_HP_BPC_mixer }, + .init_verbs = { alc262_HP_BPC_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_bpc_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_BPC_D7000_WF] = { + .mixers = { alc262_HP_BPC_WildWest_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_wildwest_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_BPC_D7000_WL] = { + .mixers = { alc262_HP_BPC_WildWest_mixer, + alc262_HP_BPC_WildWest_option_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_wildwest_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_TC_T5735] = { + .mixers = { alc262_hp_t5735_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_t5735_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_RP5700] = { + .mixers = { alc262_hp_rp5700_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_hp_rp5700_capture_source, + }, + [ALC262_BENQ_ED8] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_SONY_ASSAMD] = { + .mixers = { alc262_sony_mixer }, + .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_BENQ_T31] = { + .mixers = { alc262_benq_t31_mixer }, + .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, + alc_hp15_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_ULTRA] = { + .mixers = { alc262_ultra_mixer }, + .cap_mixer = alc262_ultra_capture_mixer, + .init_verbs = { alc262_ultra_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_ultra_capture_source, + .adc_nids = alc262_adc_nids, /* ADC0 */ + .capsrc_nids = alc262_capsrc_nids, + .num_adc_nids = 1, /* single ADC */ + .unsol_event = alc262_ultra_unsol_event, + .init_hook = alc262_ultra_automute, + }, + [ALC262_LENOVO_3000] = { + .mixers = { alc262_lenovo_3000_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_lenovo_3000_unsol_verbs, + alc262_lenovo_3000_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_lenovo_3000_setup, + .init_hook = alc_inithook, + }, + [ALC262_NEC] = { + .mixers = { alc262_nec_mixer }, + .init_verbs = { alc262_nec_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_TOSHIBA_S06] = { + .mixers = { alc262_toshiba_s06_mixer }, + .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs, + alc262_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .capsrc_nids = alc262_dmic_capsrc_nids, + .dac_nids = alc262_dac_nids, + .adc_nids = alc262_dmic_adc_nids, /* ADC0 */ + .num_adc_nids = 1, /* single ADC */ + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_toshiba_s06_setup, + .init_hook = alc_inithook, + }, + [ALC262_TOSHIBA_RX1] = { + .mixers = { alc262_toshiba_rx1_mixer }, + .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_TYAN] = { + .mixers = { alc262_tyan_mixer }, + .init_verbs = { alc262_init_verbs, alc262_tyan_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_tyan_setup, + .init_hook = alc_hp_automute, + }, +}; + diff --git a/sound/pci/hda/alc268_quirks.c b/sound/pci/hda/alc268_quirks.c new file mode 100644 index 000000000000..be58bf2f3aec --- /dev/null +++ b/sound/pci/hda/alc268_quirks.c @@ -0,0 +1,636 @@ +/* + * ALC267/ALC268 quirk models + * included by patch_realtek.c + */ + +/* ALC268 models */ +enum { + ALC268_AUTO, + ALC267_QUANTA_IL1, + ALC268_3ST, + ALC268_TOSHIBA, + ALC268_ACER, + ALC268_ACER_DMIC, + ALC268_ACER_ASPIRE_ONE, + ALC268_DELL, + ALC268_ZEPTO, +#ifdef CONFIG_SND_DEBUG + ALC268_TEST, +#endif + ALC268_MODEL_LAST /* last tag */ +}; + +/* + * ALC268 channel source setting (2 channel) + */ +#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID +#define alc268_modes alc260_modes + +static const hda_nid_t alc268_dac_nids[2] = { + /* front, hp */ + 0x02, 0x03 +}; + +static const hda_nid_t alc268_adc_nids[2] = { + /* ADC0-1 */ + 0x08, 0x07 +}; + +static const hda_nid_t alc268_adc_nids_alt[1] = { + /* ADC0 */ + 0x08 +}; + +static const hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; + +static const struct snd_kcontrol_new alc268_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", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc268_toshiba_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc268_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* Toshiba specific */ +static const struct hda_verb alc268_toshiba_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* Acer specific */ +/* bind volumes of both NID 0x02 and 0x03 */ +static const struct hda_bind_ctls alc268_acer_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static void alc268_acer_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +#define alc268_acer_master_sw_get alc262_hp_master_sw_get +#define alc268_acer_master_sw_put alc262_hp_master_sw_put + +static const struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x15, + .info = snd_ctl_boolean_mono_info, + .get = alc268_acer_master_sw_get, + .put = alc268_acer_master_sw_put, + }, + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc268_acer_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .info = snd_ctl_boolean_mono_info, + .get = alc268_acer_master_sw_get, + .put = alc268_acer_master_sw_put, + }, + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .info = snd_ctl_boolean_mono_info, + .get = alc268_acer_master_sw_get, + .put = alc268_acer_master_sw_put, + }, + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc268_acer_aspire_one_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x06}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017}, + { } +}; + +static const struct hda_verb alc268_acer_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +/* unsolicited event for HP jack sensing */ +#define alc268_toshiba_setup alc262_hippo_setup + +static void alc268_acer_lc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +static const struct snd_kcontrol_new alc268_dell_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc268_dell_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + { } +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc268_dell_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc267_quanta_il1_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + { } +}; + +static void alc267_quanta_il1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc268_base_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}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, 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}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* Unmute Selector 23h,24h and set the default input to mic-in */ + + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + { } +}; + +/* only for model=test */ +#ifdef CONFIG_SND_DEBUG +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc268_volume_init_verbs[] = { + /* set output DAC */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; +#endif /* CONFIG_SND_DEBUG */ + +static const struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc268_capture_alt_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + _DEFINE_CAPSRC(1), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc268_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT), + _DEFINE_CAPSRC(2), + { } /* end */ +}; + +static const struct hda_input_mux alc268_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x3 }, + }, +}; + +static const struct hda_input_mux alc268_acer_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc268_acer_dmic_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x6 }, + { "Line", 0x2 }, + }, +}; + +#ifdef CONFIG_SND_DEBUG +static const struct snd_kcontrol_new alc268_test_mixer[] = { + /* Volume widgets */ + HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), + HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), + HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), + HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), + HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), + /* The below appears problematic on some hardwares */ + /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ + HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), + + /* Modes for retasking pin widgets */ + ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), + + /* Controls for GPIO pins, assuming they are configured as outputs */ + ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), + ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), + ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), + ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), + + /* Switches to allow the digital SPDIF output pin to be enabled. + * The ALC268 does not have an SPDIF input. + */ + ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01), + + /* A switch allowing EAPD to be enabled. Some laptops seem to use + * this output to turn on an external amplifier. + */ + ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), + ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), + + { } /* end */ +}; +#endif + +/* + * configuration and preset + */ +static const char * const alc268_models[ALC268_MODEL_LAST] = { + [ALC267_QUANTA_IL1] = "quanta-il1", + [ALC268_3ST] = "3stack", + [ALC268_TOSHIBA] = "toshiba", + [ALC268_ACER] = "acer", + [ALC268_ACER_DMIC] = "acer-dmic", + [ALC268_ACER_ASPIRE_ONE] = "acer-aspire", + [ALC268_DELL] = "dell", + [ALC268_ZEPTO] = "zepto", +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = "test", +#endif + [ALC268_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), + 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(0x1025, 0x015b, "Acer Aspire One", + ALC268_ACER_ASPIRE_ONE), + SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), + SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron 910", ALC268_AUTO), + SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0, + "Dell Inspiron Mini9/Vostro A90", ALC268_DELL), + /* almost compatible with toshiba but with optional digital outs; + * auto-probing seems working fine + */ + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series", + ALC268_AUTO), + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), + SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), + SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), + {} +}; + +/* Toshiba laptops have no unique PCI SSID but only codec SSID */ +static const struct snd_pci_quirk alc268_ssid_cfg_tbl[] = { + SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO), + SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO), + SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05", + ALC268_TOSHIBA), + {} +}; + +static const struct alc_config_preset alc268_presets[] = { + [ALC267_QUANTA_IL1] = { + .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer, + alc268_capture_nosrc_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc267_quanta_il1_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, + .unsol_event = alc_sku_unsol_event, + .setup = alc267_quanta_il1_setup, + .init_hook = alc_inithook, + }, + [ALC268_3ST] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_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, + .capsrc_nids = alc268_capsrc_nids, + .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_toshiba_mixer, alc268_capture_alt_mixer, + alc268_beep_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, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_toshiba_setup, + .init_hook = alc_inithook, + }, + [ALC268_ACER] = { + .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, + alc268_beep_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, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_acer_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_acer_setup, + .init_hook = alc_inithook, + }, + [ALC268_ACER_DMIC] = { + .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer, + alc268_beep_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, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_acer_dmic_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_acer_setup, + .init_hook = alc_inithook, + }, + [ALC268_ACER_ASPIRE_ONE] = { + .mixers = { alc268_acer_aspire_one_mixer, + alc268_beep_mixer, + alc268_capture_nosrc_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_aspire_one_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, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_acer_lc_setup, + .init_hook = alc_inithook, + }, + [ALC268_DELL] = { + .mixers = { alc268_dell_mixer, alc268_beep_mixer, + alc268_capture_nosrc_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, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_dell_setup, + .init_hook = alc_inithook, + }, + [ALC268_ZEPTO] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_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, + .capsrc_nids = alc268_capsrc_nids, + .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, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_toshiba_setup, + .init_hook = alc_inithook, + }, +#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, + alc268_beep_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, + .capsrc_nids = alc268_capsrc_nids, + .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 +}; + diff --git a/sound/pci/hda/alc269_quirks.c b/sound/pci/hda/alc269_quirks.c new file mode 100644 index 000000000000..14fdcf29b154 --- /dev/null +++ b/sound/pci/hda/alc269_quirks.c @@ -0,0 +1,681 @@ +/* + * ALC269/ALC270/ALC275/ALC276 quirk models + * included by patch_realtek.c + */ + +/* ALC269 models */ +enum { + ALC269_AUTO, + ALC269_BASIC, + ALC269_QUANTA_FL1, + ALC269_AMIC, + ALC269_DMIC, + ALC269VB_AMIC, + ALC269VB_DMIC, + ALC269_FUJITSU, + ALC269_LIFEBOOK, + ALC271_ACER, + ALC269_MODEL_LAST /* last tag */ +}; + +/* + * ALC269 channel source setting (2 channel) + */ +#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID + +#define alc269_dac_nids alc260_dac_nids + +static const hda_nid_t alc269_adc_nids[1] = { + /* ADC1 */ + 0x08, +}; + +static const hda_nid_t alc269_capsrc_nids[1] = { + 0x23, +}; + +static const hda_nid_t alc269vb_adc_nids[1] = { + /* ADC1 */ + 0x09, +}; + +static const hda_nid_t alc269vb_capsrc_nids[1] = { + 0x22, +}; + +#define alc269_modes alc260_modes +#define alc269_capture_source alc880_lg_lw_capture_source + +static const 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 Volume", 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 Volume", 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 */ +}; + +static const struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + 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 Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc269_lifebook_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + 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 Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x1b, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc269_laptop_mixer[] = { + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269vb_laptop_mixer[] = { + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269_asus_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT), + { } /* end */ +}; + +/* capture mixer elements */ +static const struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +/* FSC amilo */ +#define alc269_fujitsu_mixer alc269_laptop_mixer + +static const struct hda_verb alc269_quanta_fl1_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +}; + +static const struct hda_verb alc269_lifebook_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) +{ + alc_hp_automute(codec); + + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_COEF_INDEX, 0x0c); + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x680); + + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_COEF_INDEX, 0x0c); + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x480); +} + +#define alc269_lifebook_speaker_automute \ + alc269_quanta_fl1_speaker_automute + +static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) +{ + unsigned int present_laptop; + unsigned int present_dock; + + present_laptop = snd_hda_jack_detect(codec, 0x18); + present_dock = snd_hda_jack_detect(codec, 0x1b); + + /* Laptop mic port overrides dock mic port, design decision */ + if (present_dock) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x3); + if (present_laptop) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x0); + if (!present_dock && !present_laptop) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x1); +} + +static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC_HP_EVENT: + alc269_quanta_fl1_speaker_automute(codec); + break; + case ALC_MIC_EVENT: + alc_mic_automute(codec); + break; + } +} + +static void alc269_lifebook_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc269_lifebook_speaker_automute(codec); + if ((res >> 26) == ALC_MIC_EVENT) + alc269_lifebook_mic_autoswitch(codec); +} + +static void alc269_quanta_fl1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) +{ + alc269_quanta_fl1_speaker_automute(codec); + alc_mic_automute(codec); +} + +static void alc269_lifebook_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.hp_pins[1] = 0x1a; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; +} + +static void alc269_lifebook_init_hook(struct hda_codec *codec) +{ + alc269_lifebook_speaker_automute(codec); + alc269_lifebook_mic_autoswitch(codec); +} + +static const struct hda_verb alc269_laptop_dmic_init_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc269_laptop_amic_init_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc269vb_laptop_dmic_init_verbs[] = { + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x06}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc269vb_laptop_amic_init_verbs[] = { + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc271_acer_dmic_verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x22, AC_VERB_SET_CONNECT_SEL, 6}, + { } +}; + +static void alc269_laptop_amic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc269_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +static void alc269vb_laptop_amic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc269_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* + * Set up output mixers (0x02 - 0x03) + */ + /* 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}, + + /* FIXME: use Mux-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc269vb_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* + * Set up output mixers (0x02 - 0x03) + */ + /* 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)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, 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}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* FIXME: use Mux-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x22, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + * configuration and preset + */ +static const char * const alc269_models[ALC269_MODEL_LAST] = { + [ALC269_BASIC] = "basic", + [ALC269_QUANTA_FL1] = "quanta", + [ALC269_AMIC] = "laptop-amic", + [ALC269_DMIC] = "laptop-dmic", + [ALC269_FUJITSU] = "fujitsu", + [ALC269_LIFEBOOK] = "lifebook", + [ALC269_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc269_cfg_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), + SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER), + SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", + ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", + ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", + ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), + SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO), + SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), + SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), + SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), + SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC), + SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC), + SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC), + {} +}; + +static const struct alc_config_preset alc269_presets[] = { + [ALC269_BASIC] = { + .mixers = { alc269_base_mixer }, + .init_verbs = { alc269_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + }, + [ALC269_QUANTA_FL1] = { + .mixers = { alc269_quanta_fl1_mixer }, + .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + .unsol_event = alc269_quanta_fl1_unsol_event, + .setup = alc269_quanta_fl1_setup, + .init_hook = alc269_quanta_fl1_init_hook, + }, + [ALC269_AMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_analog_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_laptop_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269_laptop_amic_setup, + .init_hook = alc_inithook, + }, + [ALC269_DMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_digital_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc_inithook, + }, + [ALC269VB_AMIC] = { + .mixers = { alc269vb_laptop_mixer }, + .cap_mixer = alc269vb_laptop_analog_capture_mixer, + .init_verbs = { alc269vb_init_verbs, + alc269vb_laptop_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269vb_laptop_amic_setup, + .init_hook = alc_inithook, + }, + [ALC269VB_DMIC] = { + .mixers = { alc269vb_laptop_mixer }, + .cap_mixer = alc269vb_laptop_digital_capture_mixer, + .init_verbs = { alc269vb_init_verbs, + alc269vb_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269vb_laptop_dmic_setup, + .init_hook = alc_inithook, + }, + [ALC269_FUJITSU] = { + .mixers = { alc269_fujitsu_mixer }, + .cap_mixer = alc269_laptop_digital_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc_inithook, + }, + [ALC269_LIFEBOOK] = { + .mixers = { alc269_lifebook_mixer }, + .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + .unsol_event = alc269_lifebook_unsol_event, + .setup = alc269_lifebook_setup, + .init_hook = alc269_lifebook_init_hook, + }, + [ALC271_ACER] = { + .mixers = { alc269_asus_mixer }, + .cap_mixer = alc269vb_laptop_digital_capture_mixer, + .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .adc_nids = alc262_dmic_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids), + .capsrc_nids = alc262_dmic_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + .dig_out_nid = ALC880_DIGOUT_NID, + .unsol_event = alc_sku_unsol_event, + .setup = alc269vb_laptop_dmic_setup, + .init_hook = alc_inithook, + }, +}; + diff --git a/sound/pci/hda/alc662_quirks.c b/sound/pci/hda/alc662_quirks.c new file mode 100644 index 000000000000..e69a6ea3083a --- /dev/null +++ b/sound/pci/hda/alc662_quirks.c @@ -0,0 +1,1408 @@ +/* + * ALC662/ALC663/ALC665/ALC670 quirk models + * included by patch_realtek.c + */ + +/* ALC662 models */ +enum { + ALC662_AUTO, + ALC662_3ST_2ch_DIG, + ALC662_3ST_6ch_DIG, + ALC662_3ST_6ch, + ALC662_5ST_DIG, + ALC662_LENOVO_101E, + ALC662_ASUS_EEEPC_P701, + ALC662_ASUS_EEEPC_EP20, + ALC663_ASUS_M51VA, + ALC663_ASUS_G71V, + ALC663_ASUS_H13, + ALC663_ASUS_G50V, + ALC662_ECS, + ALC663_ASUS_MODE1, + ALC662_ASUS_MODE2, + ALC663_ASUS_MODE3, + ALC663_ASUS_MODE4, + ALC663_ASUS_MODE5, + ALC663_ASUS_MODE6, + ALC663_ASUS_MODE7, + ALC663_ASUS_MODE8, + ALC272_DELL, + ALC272_DELL_ZM1, + ALC272_SAMSUNG_NC10, + ALC662_MODEL_LAST, +}; + +#define ALC662_DIGOUT_NID 0x06 +#define ALC662_DIGIN_NID 0x0a + +static const hda_nid_t alc662_dac_nids[3] = { + /* front, rear, clfe */ + 0x02, 0x03, 0x04 +}; + +static const hda_nid_t alc272_dac_nids[2] = { + 0x02, 0x03 +}; + +static const hda_nid_t alc662_adc_nids[2] = { + /* ADC1-2 */ + 0x09, 0x08 +}; + +static const hda_nid_t alc272_adc_nids[1] = { + /* ADC1-2 */ + 0x08, +}; + +static const hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 }; +static const hda_nid_t alc272_capsrc_nids[1] = { 0x23 }; + + +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ +static const struct hda_input_mux alc662_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc662_lenovo_101e_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc663_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +#if 0 /* set to 1 for testing other input sources below */ +static const struct hda_input_mux alc272_nc10_capture_source = { + .num_items = 16, + .items = { + { "Autoselect Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "In-0x02", 0x2 }, + { "In-0x03", 0x3 }, + { "In-0x04", 0x4 }, + { "In-0x05", 0x5 }, + { "In-0x06", 0x6 }, + { "In-0x07", 0x7 }, + { "In-0x08", 0x8 }, + { "In-0x09", 0x9 }, + { "In-0x0a", 0x0a }, + { "In-0x0b", 0x0b }, + { "In-0x0c", 0x0c }, + { "In-0x0d", 0x0d }, + { "In-0x0e", 0x0e }, + { "In-0x0f", 0x0f }, + }, +}; +#endif + +/* + * 2ch mode + */ +static const struct hda_channel_mode alc662_3ST_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc662_3ST_ch2_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc662_3ST_ch6_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc662_3ST_6ch_modes[2] = { + { 2, alc662_3ST_ch2_init }, + { 6, alc662_3ST_ch6_init }, +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc662_sixstack_ch6_init[] = { + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc662_sixstack_ch8_init[] = { + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static const struct hda_channel_mode alc662_5stack_modes[2] = { + { 2, alc662_sixstack_ch6_init }, + { 6, alc662_sixstack_ch8_init }, +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ + +static const struct snd_kcontrol_new alc662_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + /*Input mixer control */ + HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 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, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = { + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT), + 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), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc663_asus_one_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_m51va_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_tree_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_four_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_1bjd_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_two_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc663_asus_two_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", + &alc663_asus_two_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc663_g71v_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc663_g50v_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_mode7_mixer[] = { + HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), + HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), + HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc663_mode8_mixer[] = { + HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), + HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), + HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + +static const struct snd_kcontrol_new alc662_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static const struct hda_verb alc662_init_verbs[] = { + /* ADC: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {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)}, + + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + { } +}; + +static const struct hda_verb alc662_eapd_init_verbs[] = { + /* always trun on EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc662_sue_init_verbs[] = { + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_FRONT_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc662_eeepc_sue_init_verbs[] = { + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +/* Set Unsolicited Event*/ +static const struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_m51va_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_21jd_amic_init_verbs[] = { + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc662_1bjd_amic_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_15jd_amic_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_g71v_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ + /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */ + + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_FRONT_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_g50v_init_verbs[] = { + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ + + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc662_ecs_init_verbs[] = { + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc272_dell_zm1_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc272_dell_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_mode7_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_mode8_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct snd_kcontrol_new alc662_auto_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc272_auto_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + { } /* end */ +}; + +static void alc662_lenovo_101e_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.line_out_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->detect_line = 1; + spec->automute_lines = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc662_eeepc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc262_hippo1_setup(codec); + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc662_eeepc_ep20_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc663_m51va_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +/* ***************** Mode1 ******************************/ +static void alc663_mode1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode2 ******************************/ +static void alc662_mode2_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode3 ******************************/ +static void alc663_mode3_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode4 ******************************/ +static void alc663_mode4_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute_mixer_nid[1] = 0x0e; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode5 ******************************/ +static void alc663_mode5_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute_mixer_nid[1] = 0x0e; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode6 ******************************/ +static void alc663_mode6_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode7 ******************************/ +static void alc663_mode7_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode8 ******************************/ +static void alc663_mode8_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.hp_pins[1] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +static void alc663_g71v_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.line_out_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; + spec->detect_line = 1; + spec->automute_lines = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +#define alc663_g50v_setup alc663_m51va_setup + +static const struct snd_kcontrol_new alc662_ecs_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + + HDA_CODEC_VOLUME("Mic/LineIn Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT), + + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc272_nc10_mixer[] = { + /* Master Playback automatically created from Speaker and Headphone */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + 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 Volume", 0x18, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + + +/* + * configuration and preset + */ +static const char * const alc662_models[ALC662_MODEL_LAST] = { + [ALC662_3ST_2ch_DIG] = "3stack-dig", + [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig", + [ALC662_3ST_6ch] = "3stack-6ch", + [ALC662_5ST_DIG] = "5stack-dig", + [ALC662_LENOVO_101E] = "lenovo-101e", + [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", + [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", + [ALC662_ECS] = "ecs", + [ALC663_ASUS_M51VA] = "m51va", + [ALC663_ASUS_G71V] = "g71v", + [ALC663_ASUS_H13] = "h13", + [ALC663_ASUS_G50V] = "g50v", + [ALC663_ASUS_MODE1] = "asus-mode1", + [ALC662_ASUS_MODE2] = "asus-mode2", + [ALC663_ASUS_MODE3] = "asus-mode3", + [ALC663_ASUS_MODE4] = "asus-mode4", + [ALC663_ASUS_MODE5] = "asus-mode5", + [ALC663_ASUS_MODE6] = "asus-mode6", + [ALC663_ASUS_MODE7] = "asus-mode7", + [ALC663_ASUS_MODE8] = "asus-mode8", + [ALC272_DELL] = "dell", + [ALC272_DELL_ZM1] = "dell-zm1", + [ALC272_SAMSUNG_NC10] = "samsung-nc10", + [ALC662_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc662_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), + SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL), + SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1), + SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA), + SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), + SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), + /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/ + SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), + /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/ + SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA), + SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4), + SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), + SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), + SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), + SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), + SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), + SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), + SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", + ALC663_ASUS_H13), + SND_PCI_QUIRK(0x1991, 0x5628, "Ordissimo EVE", ALC662_LENOVO_101E), + {} +}; + +static const struct alc_config_preset alc662_presets[] = { + [ALC662_3ST_2ch_DIG] = { + .mixers = { alc662_3ST_2ch_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .dig_in_nid = ALC662_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .input_mux = &alc662_capture_source, + }, + [ALC662_3ST_6ch_DIG] = { + .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .dig_in_nid = ALC662_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc662_capture_source, + }, + [ALC662_3ST_6ch] = { + .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc662_capture_source, + }, + [ALC662_5ST_DIG] = { + .mixers = { alc662_base_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .dig_in_nid = ALC662_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), + .channel_mode = alc662_5stack_modes, + .input_mux = &alc662_capture_source, + }, + [ALC662_LENOVO_101E] = { + .mixers = { alc662_lenovo_101e_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .input_mux = &alc662_lenovo_101e_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_lenovo_101e_setup, + .init_hook = alc_inithook, + }, + [ALC662_ASUS_EEEPC_P701] = { + .mixers = { alc662_eeepc_p701_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_eeepc_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_eeepc_setup, + .init_hook = alc_inithook, + }, + [ALC662_ASUS_EEEPC_EP20] = { + .mixers = { alc662_eeepc_ep20_mixer, + alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_eeepc_ep20_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .input_mux = &alc662_lenovo_101e_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_eeepc_ep20_setup, + .init_hook = alc_inithook, + }, + [ALC662_ECS] = { + .mixers = { alc662_ecs_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_ecs_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_eeepc_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_M51VA] = { + .mixers = { alc663_m51va_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_m51va_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_m51va_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_G71V] = { + .mixers = { alc663_g71v_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_g71v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_g71v_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_H13] = { + .mixers = { alc663_m51va_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_m51va_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .setup = alc663_m51va_setup, + .unsol_event = alc_sku_unsol_event, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_G50V] = { + .mixers = { alc663_g50v_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_g50v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .input_mux = &alc663_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_g50v_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE1] = { + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_21jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode1_setup, + .init_hook = alc_inithook, + }, + [ALC662_ASUS_MODE2] = { + .mixers = { alc662_1bjd_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_1bjd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_mode2_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE3] = { + .mixers = { alc663_two_hp_m1_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_two_hp_amic_m1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode3_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE4] = { + .mixers = { alc663_asus_21jd_clfe_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_21jd_amic_init_verbs}, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode4_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE5] = { + .mixers = { alc663_asus_15jd_clfe_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_15jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode5_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE6] = { + .mixers = { alc663_two_hp_m2_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_two_hp_amic_m2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode6_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE7] = { + .mixers = { alc663_mode7_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_mode7_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode7_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE8] = { + .mixers = { alc663_mode8_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_mode8_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode8_setup, + .init_hook = alc_inithook, + }, + [ALC272_DELL] = { + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc272_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc272_dell_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .adc_nids = alc272_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc272_adc_nids), + .capsrc_nids = alc272_capsrc_nids, + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_m51va_setup, + .init_hook = alc_inithook, + }, + [ALC272_DELL_ZM1] = { + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc272_dell_zm1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .adc_nids = alc662_adc_nids, + .num_adc_nids = 1, + .capsrc_nids = alc662_capsrc_nids, + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_m51va_setup, + .init_hook = alc_inithook, + }, + [ALC272_SAMSUNG_NC10] = { + .mixers = { alc272_nc10_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_21jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + /*.input_mux = &alc272_nc10_capture_source,*/ + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode4_setup, + .init_hook = alc_inithook, + }, +}; + + diff --git a/sound/pci/hda/alc680_quirks.c b/sound/pci/hda/alc680_quirks.c new file mode 100644 index 000000000000..0eeb227c7bc2 --- /dev/null +++ b/sound/pci/hda/alc680_quirks.c @@ -0,0 +1,222 @@ +/* + * ALC680 quirk models + * included by patch_realtek.c + */ + +/* ALC680 models */ +enum { + ALC680_AUTO, + ALC680_BASE, + ALC680_MODEL_LAST, +}; + +#define ALC680_DIGIN_NID ALC880_DIGIN_NID +#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID +#define alc680_modes alc260_modes + +static const hda_nid_t alc680_dac_nids[3] = { + /* Lout1, Lout2, hp */ + 0x02, 0x03, 0x04 +}; + +static const hda_nid_t alc680_adc_nids[3] = { + /* ADC0-2 */ + /* DMIC, MIC, Line-in*/ + 0x07, 0x08, 0x09 +}; + +/* + * Analog capture ADC cgange + */ +static hda_nid_t alc680_get_cur_adc(struct hda_codec *codec) +{ + static hda_nid_t pins[] = {0x18, 0x19}; + static hda_nid_t adcs[] = {0x08, 0x09}; + int i; + + for (i = 0; i < ARRAY_SIZE(pins); i++) { + if (!is_jack_detectable(codec, pins[i])) + continue; + if (snd_hda_jack_detect(codec, pins[i])) + return adcs[i]; + } + return 0x07; +} + +static void alc680_rec_autoswitch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = alc680_get_cur_adc(codec); + if (spec->cur_adc && nid != spec->cur_adc) { + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = nid; + snd_hda_codec_setup_stream(codec, nid, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } +} + +static int alc680_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; + hda_nid_t nid = alc680_get_cur_adc(codec); + + spec->cur_adc = nid; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); + return 0; +} + +static int alc680_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 const struct hda_pcm_stream alc680_pcm_analog_auto_capture = { + .substreams = 1, /* can be overridden */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ + .ops = { + .prepare = alc680_capture_pcm_prepare, + .cleanup = alc680_capture_pcm_cleanup + }, +}; + +static const 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("Internal Mic Boost Volume", 0x12, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct hda_bind_ctls alc680_bind_cap_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc680_bind_cap_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc680_master_capture_mixer[] = { + HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol), + HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch), + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc680_init_verbs[] = { + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {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}, + + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc680_base_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x16; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.num_inputs = 2; + spec->autocfg.inputs[0].pin = 0x18; + spec->autocfg.inputs[0].type = AUTO_PIN_MIC; + spec->autocfg.inputs[1].pin = 0x19; + spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc680_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc_hp_automute(codec); + if ((res >> 26) == ALC_MIC_EVENT) + alc680_rec_autoswitch(codec); +} + +static void alc680_inithook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc680_rec_autoswitch(codec); +} + +/* + * configuration and preset + */ +static const char * const alc680_models[ALC680_MODEL_LAST] = { + [ALC680_BASE] = "base", + [ALC680_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc680_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE), + {} +}; + +static const struct alc_config_preset alc680_presets[] = { + [ALC680_BASE] = { + .mixers = { alc680_base_mixer }, + .cap_mixer = alc680_master_capture_mixer, + .init_verbs = { alc680_init_verbs }, + .num_dacs = ARRAY_SIZE(alc680_dac_nids), + .dac_nids = alc680_dac_nids, + .dig_out_nid = ALC680_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc680_modes), + .channel_mode = alc680_modes, + .unsol_event = alc680_unsol_event, + .setup = alc680_base_setup, + .init_hook = alc680_inithook, + + }, +}; diff --git a/sound/pci/hda/alc861_quirks.c b/sound/pci/hda/alc861_quirks.c new file mode 100644 index 000000000000..d719ec6350eb --- /dev/null +++ b/sound/pci/hda/alc861_quirks.c @@ -0,0 +1,725 @@ +/* + * ALC660/ALC861 quirk models + * included by patch_realtek.c + */ + +/* ALC861 models */ +enum { + ALC861_AUTO, + ALC861_3ST, + ALC660_3ST, + ALC861_3ST_DIG, + ALC861_6ST_DIG, + ALC861_UNIWILL_M31, + ALC861_TOSHIBA, + ALC861_ASUS, + ALC861_ASUS_LAPTOP, + ALC861_MODEL_LAST, +}; + +/* + * ALC861 channel source setting (2/6 channel selection for 3-stack) + */ + +/* + * set the path ways for 2 channel output + * need to set the codec line out and mic 1 pin widgets to inputs + */ +static const struct hda_verb alc861_threestack_ch2_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set pin widget 18h (mic1/2) for input, for mic also enable + * the vref + */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif + { } /* end */ +}; +/* + * 6ch mode + * need to set the codec line out and mic 1 pin widgets to outputs + */ +static const struct hda_verb alc861_threestack_ch6_init[] = { + /* set pin widget 1Ah (line in) for output (Back Surround)*/ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* set pin widget 18h (mic1) for output (CLFE)*/ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + + { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif + { } /* end */ +}; + +static const struct hda_channel_mode alc861_threestack_modes[2] = { + { 2, alc861_threestack_ch2_init }, + { 6, alc861_threestack_ch6_init }, +}; +/* Set mic1 as input and unmute the mixer */ +static const struct hda_verb alc861_uniwill_m31_ch2_init[] = { + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { } /* end */ +}; +/* Set mic1 as output and mute mixer */ +static const struct hda_verb alc861_uniwill_m31_ch4_init[] = { + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { } /* end */ +}; + +static const struct hda_channel_mode alc861_uniwill_m31_modes[2] = { + { 2, alc861_uniwill_m31_ch2_init }, + { 4, alc861_uniwill_m31_ch4_init }, +}; + +/* Set mic1 and line-in as input and unmute the mixer */ +static const struct hda_verb alc861_asus_ch2_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set pin widget 18h (mic1/2) for input, for mic also enable + * the vref + */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif + { } /* end */ +}; +/* Set mic1 nad line-in as output and mute mixer */ +static const struct hda_verb alc861_asus_ch6_init[] = { + /* set pin widget 1Ah (line in) for output (Back Surround)*/ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ + /* set pin widget 18h (mic1) for output (CLFE)*/ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ + { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif + { } /* end */ +}; + +static const struct hda_channel_mode alc861_asus_modes[2] = { + { 2, alc861_asus_ch2_init }, + { 6, alc861_asus_ch6_init }, +}; + +/* patch-ALC861 */ + +static const struct snd_kcontrol_new alc861_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), + + /*Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_3ST_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ + + /* Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + .private_value = ARRAY_SIZE(alc861_threestack_modes), + }, + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_toshiba_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ + + /* Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes), + }, + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_asus_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), + + /* Input mixer control */ + HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), + + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + .private_value = ARRAY_SIZE(alc861_asus_modes), + }, + { } +}; + +/* additional mixer */ +static const struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + { } +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc861_base_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute DAC0~3 & spdif out*/ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + + { } +}; + +static const struct hda_verb alc861_threestack_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + +static const struct hda_verb alc861_uniwill_m31_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + /* this has to be set to VREF80 */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + +static const struct hda_verb alc861_asus_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) + * according to codec#0 this is the HP jack + */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */ + /* route front PCM to HP */ + { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + /* this has to be set to VREF80 */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + +/* additional init verbs for ASUS laptops */ +static const struct hda_verb alc861_asus_laptop_init_verbs[] = { + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */ + { } +}; + +static const struct hda_verb alc861_toshiba_init_verbs[] = { + {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc861_toshiba_automute(struct hda_codec *codec) +{ + unsigned int present = snd_hda_jack_detect(codec, 0x0f); + + snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, + HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); +} + +static void alc861_toshiba_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc861_toshiba_automute(codec); +} + +#define ALC861_DIGOUT_NID 0x07 + +static const struct hda_channel_mode alc861_8ch_modes[1] = { + { 8, NULL } +}; + +static const hda_nid_t alc861_dac_nids[4] = { + /* front, surround, clfe, side */ + 0x03, 0x06, 0x05, 0x04 +}; + +static const hda_nid_t alc660_dac_nids[3] = { + /* front, clfe, surround */ + 0x03, 0x05, 0x06 +}; + +static const hda_nid_t alc861_adc_nids[1] = { + /* ADC0-2 */ + 0x08, +}; + +static const struct hda_input_mux alc861_capture_source = { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x3 }, + { "Line", 0x1 }, + { "CD", 0x4 }, + { "Mixer", 0x5 }, + }, +}; + +/* + * configuration and preset + */ +static const char * const alc861_models[ALC861_MODEL_LAST] = { + [ALC861_3ST] = "3stack", + [ALC660_3ST] = "3stack-660", + [ALC861_3ST_DIG] = "3stack-dig", + [ALC861_6ST_DIG] = "6stack-dig", + [ALC861_UNIWILL_M31] = "uniwill-m31", + [ALC861_TOSHIBA] = "toshiba", + [ALC861_ASUS] = "asus", + [ALC861_ASUS_LAPTOP] = "asus-laptop", + [ALC861_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc861_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), + SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), + SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), + /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) + * Any other models that need this preset? + */ + /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ + SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), + SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), + SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), + /* FIXME: the below seems conflict */ + /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ + SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), + SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), + {} +}; + +static const struct alc_config_preset alc861_presets[] = { + [ALC861_3ST] = { + .mixers = { alc861_3ST_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_3ST_DIG] = { + .mixers = { alc861_base_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_6ST_DIG] = { + .mixers = { alc861_base_mixer }, + .init_verbs = { alc861_base_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes), + .channel_mode = alc861_8ch_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC660_3ST] = { + .mixers = { alc861_3ST_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660_dac_nids), + .dac_nids = alc660_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_UNIWILL_M31] = { + .mixers = { alc861_uniwill_m31_mixer }, + .init_verbs = { alc861_uniwill_m31_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes), + .channel_mode = alc861_uniwill_m31_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_TOSHIBA] = { + .mixers = { alc861_toshiba_mixer }, + .init_verbs = { alc861_base_init_verbs, + alc861_toshiba_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + .unsol_event = alc861_toshiba_unsol_event, + .init_hook = alc861_toshiba_automute, + }, + [ALC861_ASUS] = { + .mixers = { alc861_asus_mixer }, + .init_verbs = { alc861_asus_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_asus_modes), + .channel_mode = alc861_asus_modes, + .need_dac_fix = 1, + .hp_nid = 0x06, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_ASUS_LAPTOP] = { + .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer }, + .init_verbs = { alc861_asus_init_verbs, + alc861_asus_laptop_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, +}; + diff --git a/sound/pci/hda/alc861vd_quirks.c b/sound/pci/hda/alc861vd_quirks.c new file mode 100644 index 000000000000..8f28450f41f8 --- /dev/null +++ b/sound/pci/hda/alc861vd_quirks.c @@ -0,0 +1,605 @@ +/* + * ALC660-VD/ALC861-VD quirk models + * included by patch_realtek.c + */ + +/* ALC861-VD models */ +enum { + ALC861VD_AUTO, + ALC660VD_3ST, + ALC660VD_3ST_DIG, + ALC660VD_ASUS_V1S, + ALC861VD_3ST, + ALC861VD_3ST_DIG, + ALC861VD_6ST_DIG, + ALC861VD_LENOVO, + ALC861VD_DALLAS, + ALC861VD_HP, + ALC861VD_MODEL_LAST, +}; + +#define ALC861VD_DIGOUT_NID 0x06 + +static const hda_nid_t alc861vd_dac_nids[4] = { + /* front, surr, clfe, side surr */ + 0x02, 0x03, 0x04, 0x05 +}; + +/* dac_nids for ALC660vd are in a different order - according to + * Realtek's driver. + * This should probably result in a different mixer for 6stack models + * of ALC660vd codecs, but for now there is only 3stack mixer + * - and it is the same as in 861vd. + * adc_nids in ALC660vd are (is) the same as in 861vd + */ +static const hda_nid_t alc660vd_dac_nids[3] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x04, 0x03 +}; + +static const hda_nid_t alc861vd_adc_nids[1] = { + /* ADC0 */ + 0x09, +}; + +static const hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; + +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ +static const struct hda_input_mux alc861vd_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc861vd_dallas_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; + +static const struct hda_input_mux alc861vd_hp_capture_source = { + .num_items = 2, + .items = { + { "Front Mic", 0x0 }, + { "ATAPI Mic", 0x1 }, + }, +}; + +/* + * 2ch mode + */ +static const struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc861vd_6stack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc861vd_6stack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static const struct hda_channel_mode alc861vd_6stack_modes[2] = { + { 6, alc861vd_6stack_ch6_init }, + { 8, alc861vd_6stack_ch8_init }, +}; + +static const struct snd_kcontrol_new alc861vd_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ +static const struct snd_kcontrol_new alc861vd_6st_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + + HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861vd_3st_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/ + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + { } /* end */ +}; + +/* Pin assignment: Speaker=0x14, HP = 0x15, + * Mic=0x18, Internal Mic = 0x19, CD = 0x1c, PC Beep = 0x1d + */ +static const struct snd_kcontrol_new alc861vd_dallas_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +/* Pin assignment: Speaker=0x14, Line-out = 0x15, + * Front Mic=0x18, ATAPI Mic = 0x19, + */ +static const struct snd_kcontrol_new alc861vd_hp_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc861vd_volume_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of + * the analog-loopback mixer widget + */ + /* 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)}, + + /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers (0x02 - 0x05) + */ + /* 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}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x05, 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_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + { } +}; + +/* + * 3-stack pin configuration: + * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b + */ +static const struct hda_verb alc861vd_3stack_init_verbs[] = { + /* + * Set pin mode and muting + */ + /* set front pin widgets 0x14 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* + * 6-stack pin configuration: + */ +static const struct hda_verb alc861vd_6stack_init_verbs[] = { + /* + * Set pin mode and muting + */ + /* set front pin widgets 0x14 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +static const struct hda_verb alc861vd_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc861vd_lenovo_unsol_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {} +}; + +static void alc861vd_lenovo_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc861vd_lenovo_init_hook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc88x_simple_mic_automute(codec); +} + +static void alc861vd_lenovo_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC_MIC_EVENT: + alc88x_simple_mic_automute(codec); + break; + default: + alc_sku_unsol_event(codec, res); + break; + } +} + +static const struct hda_verb alc861vd_dallas_verbs[] = { + {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}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + {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_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc861vd_dallas_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* + * configuration and preset + */ +static const char * const alc861vd_models[ALC861VD_MODEL_LAST] = { + [ALC660VD_3ST] = "3stack-660", + [ALC660VD_3ST_DIG] = "3stack-660-digout", + [ALC660VD_ASUS_V1S] = "asus-v1s", + [ALC861VD_3ST] = "3stack", + [ALC861VD_3ST_DIG] = "3stack-digout", + [ALC861VD_6ST_DIG] = "6stack-digout", + [ALC861VD_LENOVO] = "lenovo", + [ALC861VD_DALLAS] = "dallas", + [ALC861VD_HP] = "hp", + [ALC861VD_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), + SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), + /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */ + SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), + SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), + SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), + /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ + SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), + SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), + {} +}; + +static const struct alc_config_preset alc861vd_presets[] = { + [ALC660VD_3ST] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC660VD_3ST_DIG] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_3ST] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_3ST_DIG] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_6ST_DIG] = { + .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), + .channel_mode = alc861vd_6stack_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_LENOVO] = { + .mixers = { alc861vd_lenovo_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs, + alc861vd_eapd_verbs, + alc861vd_lenovo_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + .unsol_event = alc861vd_lenovo_unsol_event, + .setup = alc861vd_lenovo_setup, + .init_hook = alc861vd_lenovo_init_hook, + }, + [ALC861VD_DALLAS] = { + .mixers = { alc861vd_dallas_mixer }, + .init_verbs = { alc861vd_dallas_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_dallas_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc861vd_dallas_setup, + .init_hook = alc_hp_automute, + }, + [ALC861VD_HP] = { + .mixers = { alc861vd_hp_mixer }, + .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_hp_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc861vd_dallas_setup, + .init_hook = alc_hp_automute, + }, + [ALC660VD_ASUS_V1S] = { + .mixers = { alc861vd_lenovo_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs, + alc861vd_eapd_verbs, + alc861vd_lenovo_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + .unsol_event = alc861vd_lenovo_unsol_event, + .setup = alc861vd_lenovo_setup, + .init_hook = alc861vd_lenovo_init_hook, + }, +}; + diff --git a/sound/pci/hda/alc880_quirks.c b/sound/pci/hda/alc880_quirks.c new file mode 100644 index 000000000000..c844d2b59988 --- /dev/null +++ b/sound/pci/hda/alc880_quirks.c @@ -0,0 +1,1898 @@ +/* + * ALC880 quirk models + * included by patch_realtek.c + */ + +/* ALC880 board config type */ +enum { + ALC880_AUTO, + ALC880_3ST, + ALC880_3ST_DIG, + ALC880_5ST, + ALC880_5ST_DIG, + ALC880_W810, + ALC880_Z71V, + ALC880_6ST, + ALC880_6ST_DIG, + ALC880_F1734, + ALC880_ASUS, + ALC880_ASUS_DIG, + ALC880_ASUS_W1V, + ALC880_ASUS_DIG2, + ALC880_FUJITSU, + ALC880_UNIWILL_DIG, + ALC880_UNIWILL, + ALC880_UNIWILL_P53, + ALC880_CLEVO, + ALC880_TCL_S700, + ALC880_LG, + ALC880_LG_LW, + ALC880_MEDION_RIM, +#ifdef CONFIG_SND_DEBUG + ALC880_TEST, +#endif + ALC880_MODEL_LAST /* last tag */ +}; + +/* + * ALC880 3-stack model + * + * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) + * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, + * F-Mic = 0x1b, HP = 0x19 + */ + +static const hda_nid_t alc880_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x05, 0x04, 0x03 +}; + +static const hda_nid_t alc880_adc_nids[3] = { + /* ADC0-2 */ + 0x07, 0x08, 0x09, +}; + +/* The datasheet says the node 0x07 is connected from inputs, + * but it shows zero connection in the real implementation on some devices. + * Note: this is a 915GAV bug, fixed on 915GLV + */ +static const hda_nid_t alc880_adc_nids_alt[2] = { + /* ADC1-2 */ + 0x08, 0x09, +}; + +#define ALC880_DIGOUT_NID 0x06 +#define ALC880_DIGIN_NID 0x0a +#define ALC880_PIN_CD_NID 0x1c + +static const struct hda_input_mux alc880_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x3 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +/* channel source setting (2/6 channel selection for 3-stack) */ +/* 2ch mode */ +static const struct hda_verb alc880_threestack_ch2_init[] = { + /* set line-in to input, mute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + /* set mic-in to input vref 80%, mute it */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* 6ch mode */ +static const struct hda_verb alc880_threestack_ch6_init[] = { + /* set line-in to output, unmute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + /* set mic-in to output, unmute it */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } /* end */ +}; + +static const struct hda_channel_mode alc880_threestack_modes[2] = { + { 2, alc880_threestack_ch2_init }, + { 6, alc880_threestack_ch6_init }, +}; + +static const struct snd_kcontrol_new alc880_three_stack_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +/* + * ALC880 5-stack model + * + * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), + * Side = 0x02 (0xd) + * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 + * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 + */ + +/* additional mixers to alc880_three_stack_mixer */ +static const struct snd_kcontrol_new alc880_five_stack_mixer[] = { + HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT), + { } /* end */ +}; + +/* channel source setting (6/8 channel selection for 5-stack) */ +/* 6ch mode */ +static const struct hda_verb alc880_fivestack_ch6_init[] = { + /* set line-in to input, mute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* 8ch mode */ +static const struct hda_verb alc880_fivestack_ch8_init[] = { + /* set line-in to output, unmute it */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } /* end */ +}; + +static const struct hda_channel_mode alc880_fivestack_modes[2] = { + { 6, alc880_fivestack_ch6_init }, + { 8, alc880_fivestack_ch8_init }, +}; + + +/* + * ALC880 6-stack model + * + * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), + * Side = 0x05 (0x0f) + * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, + * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b + */ + +static const hda_nid_t alc880_6st_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x03, 0x04, 0x05 +}; + +static const struct hda_input_mux alc880_6stack_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +/* fixed 8-channels */ +static const struct hda_channel_mode alc880_sixstack_modes[1] = { + { 8, NULL }, +}; + +static const struct snd_kcontrol_new alc880_six_stack_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + + +/* + * ALC880 W810 model + * + * W810 has rear IO for: + * Front (DAC 02) + * Surround (DAC 03) + * Center/LFE (DAC 04) + * Digital out (06) + * + * The system also has a pair of internal speakers, and a headphone jack. + * These are both connected to Line2 on the codec, hence to DAC 02. + * + * There is a variable resistor to control the speaker or headphone + * volume. This is a hardware-only device without a software API. + * + * Plugging headphones in will disable the internal speakers. This is + * implemented in hardware, not via the driver using jack sense. In + * a similar fashion, plugging into the rear socket marked "front" will + * disable both the speakers and headphones. + * + * For input, there's a microphone jack, and an "audio in" jack. + * These may not do anything useful with this driver yet, because I + * haven't setup any initialization verbs for these yet... + */ + +static const hda_nid_t alc880_w810_dac_nids[3] = { + /* front, rear/surround, clfe */ + 0x02, 0x03, 0x04 +}; + +/* fixed 6 channels */ +static const struct hda_channel_mode alc880_w810_modes[1] = { + { 6, NULL } +}; + +/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */ +static const struct snd_kcontrol_new alc880_w810_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + + +/* + * Z710V model + * + * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) + * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), + * Line = 0x1a + */ + +static const hda_nid_t alc880_z71v_dac_nids[1] = { + 0x02 +}; +#define ALC880_Z71V_HP_DAC 0x03 + +/* fixed 2 channels */ +static const struct hda_channel_mode alc880_2_jack_modes[1] = { + { 2, NULL } +}; + +static const struct snd_kcontrol_new alc880_z71v_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + +/* + * ALC880 F1734 model + * + * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d) + * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18 + */ + +static const hda_nid_t alc880_f1734_dac_nids[1] = { + 0x03 +}; +#define ALC880_F1734_HP_DAC 0x02 + +static const struct snd_kcontrol_new alc880_f1734_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_input_mux alc880_f1734_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "CD", 0x4 }, + }, +}; + + +/* + * ALC880 ASUS model + * + * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) + * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, + * Mic = 0x18, Line = 0x1a + */ + +#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */ +#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */ + +static const struct snd_kcontrol_new alc880_asus_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +/* + * ALC880 ASUS W1V model + * + * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) + * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, + * Mic = 0x18, Line = 0x1a, Line2 = 0x1b + */ + +/* additional mixers to alc880_asus_mixer */ +static const struct snd_kcontrol_new alc880_asus_w1v_mixer[] = { + HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT), + { } /* end */ +}; + +/* TCL S700 */ +static const struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, 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("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + { } /* end */ +}; + +/* Uniwill */ +static const struct snd_kcontrol_new alc880_uniwill_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static const struct snd_kcontrol_new alc880_fujitsu_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +/* + * initialize the codec volumes, etc + */ + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc880_volume_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, 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)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + + /* + * Set up output mixers (0x0c - 0x0f) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, 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_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + { } +}; + +/* + * 3-stack pin configuration: + * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b + */ +static const struct hda_verb alc880_pin_3stack_init_verbs[] = { + /* + * preset connection lists of input pins + * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround + */ + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ + + /* + * Set pin mode and muting + */ + /* set front pin widgets 0x14 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mic2 (as headphone out) for HP output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line2 (as front mic) pin widget for input and vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* + * 5-stack pin configuration: + * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19, + * line-in/side = 0x1a, f-mic = 0x1b + */ +static const struct hda_verb alc880_pin_5stack_init_verbs[] = { + /* + * preset connection lists of input pins + * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround + */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */ + + /* + * Set pin mode and muting + */ + /* set pin widgets 0x14-0x17 for output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* unmute pins for output (no gain on this amp) */ + {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_UNMUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mic2 (as headphone out) for HP output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line2 (as front mic) pin widget for input and vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* + * W810 pin configuration: + * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b + */ +static const struct hda_verb alc880_pin_w810_init_verbs[] = { + /* hphone/speaker input selector: front DAC */ + {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + { } +}; + +/* + * Z71V pin configuration: + * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?) + */ +static const struct hda_verb alc880_pin_z71v_init_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, 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_VREF80}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* + * 6-stack pin configuration: + * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, + * f-mic = 0x19, line = 0x1a, HP = 0x1b + */ +static const struct hda_verb alc880_pin_6stack_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* + * Uniwill pin configuration: + * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19, + * line = 0x1a + */ +static const struct hda_verb alc880_uniwill_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */ + /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + + { } +}; + +/* +* Uniwill P53 +* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, + */ +static const struct hda_verb alc880_uniwill_p53_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_DCVOL_EVENT}, + + { } +}; + +static const struct hda_verb alc880_beep_init_verbs[] = { + { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) }, + { } +}; + +static void alc880_uniwill_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc880_uniwill_init_hook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc88x_simple_mic_automute(codec); +} + +static void alc880_uniwill_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Looks like the unsol event is incompatible with the standard + * definition. 4bit tag is placed at 28 bit! + */ + switch (res >> 28) { + case ALC_MIC_EVENT: + alc88x_simple_mic_automute(codec); + break; + default: + alc_sku_unsol_event(codec, res); + break; + } +} + +static void alc880_uniwill_p53_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x21, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + present &= HDA_AMP_VOLMASK; + snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, + HDA_AMP_VOLMASK, present); + snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0, + HDA_AMP_VOLMASK, present); +} + +static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Looks like the unsol event is incompatible with the standard + * definition. 4bit tag is placed at 28 bit! + */ + if ((res >> 28) == ALC_DCVOL_EVENT) + alc880_uniwill_p53_dcvol_automute(codec); + else + alc_sku_unsol_event(codec, res); +} + +/* + * F1734 pin configuration: + * HP = 0x14, speaker-out = 0x15, mic = 0x18 + */ +static const struct hda_verb alc880_pin_f1734_init_verbs[] = { + {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_DCVOL_EVENT}, + + { } +}; + +/* + * ASUS pin configuration: + * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a + */ +static const struct hda_verb alc880_pin_asus_init_verbs[] = { + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + { } +}; + +/* Enable GPIO mask and set output */ +#define alc880_gpio1_init_verbs alc_gpio1_init_verbs +#define alc880_gpio2_init_verbs alc_gpio2_init_verbs +#define alc880_gpio3_init_verbs alc_gpio3_init_verbs + +/* Clevo m520g init */ +static const struct hda_verb alc880_pin_clevo_init_verbs[] = { + /* headphone output */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* line-out */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Line-in */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* CD */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Mic1 (rear panel) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Mic2 (front panel) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* headphone */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + + { } +}; + +static const struct hda_verb alc880_pin_tcl_S700_init_verbs[] = { + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + + /* Headphone output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Front output*/ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Line In pin widget for input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + + { } +}; + +/* + * LG m1 express dual + * + * Pin assignment: + * Rear Line-In/Out (blue): 0x14 + * Build-in Mic-In: 0x15 + * Speaker-out: 0x17 + * HP-Out (green): 0x1b + * Mic-In/Out (red): 0x19 + * SPDIF-Out: 0x1e + */ + +/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */ +static const hda_nid_t alc880_lg_dac_nids[3] = { + 0x05, 0x02, 0x03 +}; + +/* seems analog CD is not working */ +static const struct hda_input_mux alc880_lg_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x1 }, + { "Line", 0x5 }, + { "Internal Mic", 0x6 }, + }, +}; + +/* 2,4,6 channel modes */ +static const struct hda_verb alc880_lg_ch2_init[] = { + /* set line-in and mic-in to input */ + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { } +}; + +static const struct hda_verb alc880_lg_ch4_init[] = { + /* set line-in to out and mic-in to input */ + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { } +}; + +static const struct hda_verb alc880_lg_ch6_init[] = { + /* set line-in and mic-in to output */ + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { } +}; + +static const struct hda_channel_mode alc880_lg_ch_modes[3] = { + { 2, alc880_lg_ch2_init }, + { 4, alc880_lg_ch4_init }, + { 6, alc880_lg_ch6_init }, +}; + +static const struct snd_kcontrol_new alc880_lg_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static const struct hda_verb alc880_lg_init_verbs[] = { + /* set capture source to mic-in */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* mute all amp mixer inputs */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + /* line-in to input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* built-in mic */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* speaker-out */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* mic-in to input */ + {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* HP-out */ + {0x13, AC_VERB_SET_CONNECT_SEL, 0x03}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* jack sense */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc880_lg_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* + * LG LW20 + * + * Pin assignment: + * Speaker-out: 0x14 + * Mic-In: 0x18 + * Built-in Mic-In: 0x19 + * Line-In: 0x1b + * HP-Out: 0x1a + * SPDIF-Out: 0x1e + */ + +static const struct hda_input_mux alc880_lg_lw_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line In", 0x2 }, + }, +}; + +#define alc880_lg_lw_modes alc880_threestack_modes + +static const struct snd_kcontrol_new alc880_lg_lw_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + 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("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static const struct hda_verb alc880_lg_lw_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ + + /* set capture source to mic-in */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + /* speaker-out */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* HP-out */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* mic-in to input */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* built-in mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* jack sense */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc880_lg_lw_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct snd_kcontrol_new alc880_medion_rim_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_input_mux alc880_medion_rim_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; + +static const struct hda_verb alc880_medion_rim_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mic2 (as headphone out) for HP output */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Internal Speaker */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc880_medion_rim_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_hp_automute(codec); + /* toggle EAPD */ + if (spec->jack_present) + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); + else + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2); +} + +static void alc880_medion_rim_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Looks like the unsol event is incompatible with the standard + * definition. 4bit tag is placed at 28 bit! + */ + if ((res >> 28) == ALC_HP_EVENT) + alc880_medion_rim_automute(codec); +} + +static void alc880_medion_rim_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static const struct hda_amp_list alc880_lg_loopbacks[] = { + { 0x0b, HDA_INPUT, 1 }, + { 0x0b, HDA_INPUT, 6 }, + { 0x0b, HDA_INPUT, 7 }, + { } /* end */ +}; +#endif + +/* + * Test configuration for debugging + * + * Almost all inputs/outputs are enabled. I/O pins can be configured via + * enum controls. + */ +#ifdef CONFIG_SND_DEBUG +static const hda_nid_t alc880_test_dac_nids[4] = { + 0x02, 0x03, 0x04, 0x05 +}; + +static const struct hda_input_mux alc880_test_capture_source = { + .num_items = 7, + .items = { + { "In-1", 0x0 }, + { "In-2", 0x1 }, + { "In-3", 0x2 }, + { "In-4", 0x3 }, + { "CD", 0x4 }, + { "Front", 0x5 }, + { "Surround", 0x6 }, + }, +}; + +static const struct hda_channel_mode alc880_test_modes[4] = { + { 2, NULL }, + { 4, NULL }, + { 6, NULL }, + { 8, NULL }, +}; + +static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { + "N/A", "Line Out", "HP Out", + "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item >= 8) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + unsigned int pin_ctl, item = 0; + + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pin_ctl & AC_PINCTL_OUT_EN) { + if (pin_ctl & AC_PINCTL_HP_EN) + item = 2; + else + item = 1; + } else if (pin_ctl & AC_PINCTL_IN_EN) { + switch (pin_ctl & AC_PINCTL_VREFEN) { + case AC_PINCTL_VREF_HIZ: item = 3; break; + case AC_PINCTL_VREF_50: item = 4; break; + case AC_PINCTL_VREF_GRD: item = 5; break; + case AC_PINCTL_VREF_80: item = 6; break; + case AC_PINCTL_VREF_100: item = 7; break; + } + } + ucontrol->value.enumerated.item[0] = item; + return 0; +} + +static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + static const unsigned int ctls[] = { + 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_50, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_80, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_100, + }; + unsigned int old_ctl, new_ctl; + + old_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + new_ctl = ctls[ucontrol->value.enumerated.item[0]]; + if (old_ctl != new_ctl) { + int val; + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + new_ctl); + val = ucontrol->value.enumerated.item[0] >= 3 ? + HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, val); + return 1; + } + return 0; +} + +static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { + "Front", "Surround", "CLFE", "Side" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + unsigned int sel; + + sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); + ucontrol->value.enumerated.item[0] = sel & 3; + return 0; +} + +static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = (hda_nid_t)kcontrol->private_value; + unsigned int sel; + + sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3; + if (ucontrol->value.enumerated.item[0] != sel) { + sel = ucontrol->value.enumerated.item[0] & 3; + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, sel); + return 1; + } + return 0; +} + +#define PIN_CTL_TEST(xname,nid) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ + .info = alc_test_pin_ctl_info, \ + .get = alc_test_pin_ctl_get, \ + .put = alc_test_pin_ctl_put, \ + .private_value = nid \ + } + +#define PIN_SRC_TEST(xname,nid) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ + .info = alc_test_pin_src_info, \ + .get = alc_test_pin_src_get, \ + .put = alc_test_pin_src_put, \ + .private_value = nid \ + } + +static const struct snd_kcontrol_new alc880_test_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + PIN_CTL_TEST("Front Pin Mode", 0x14), + PIN_CTL_TEST("Surround Pin Mode", 0x15), + PIN_CTL_TEST("CLFE Pin Mode", 0x16), + PIN_CTL_TEST("Side Pin Mode", 0x17), + PIN_CTL_TEST("In-1 Pin Mode", 0x18), + PIN_CTL_TEST("In-2 Pin Mode", 0x19), + PIN_CTL_TEST("In-3 Pin Mode", 0x1a), + PIN_CTL_TEST("In-4 Pin Mode", 0x1b), + PIN_SRC_TEST("In-1 Pin Source", 0x18), + PIN_SRC_TEST("In-2 Pin Source", 0x19), + PIN_SRC_TEST("In-3 Pin Source", 0x1a), + PIN_SRC_TEST("In-4 Pin Source", 0x1b), + HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static const struct hda_verb alc880_test_init_verbs[] = { + /* Unmute inputs of 0x0c - 0x0f */ + {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)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Vol output for 0x0c-0x0f */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Set output pins 0x14-0x17 */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Unmute output pins 0x14-0x17 */ + {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_UNMUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Set input pins 0x18-0x1c */ + {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}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Mute input pins 0x18-0x1b */ + {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}, + /* ADC set up */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Analog input/passthru */ + {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)}, + { } +}; +#endif + +/* + */ + +static const char * const alc880_models[ALC880_MODEL_LAST] = { + [ALC880_3ST] = "3stack", + [ALC880_TCL_S700] = "tcl", + [ALC880_3ST_DIG] = "3stack-digout", + [ALC880_CLEVO] = "clevo", + [ALC880_5ST] = "5stack", + [ALC880_5ST_DIG] = "5stack-digout", + [ALC880_W810] = "w810", + [ALC880_Z71V] = "z71v", + [ALC880_6ST] = "6stack", + [ALC880_6ST_DIG] = "6stack-digout", + [ALC880_ASUS] = "asus", + [ALC880_ASUS_W1V] = "asus-w1v", + [ALC880_ASUS_DIG] = "asus-dig", + [ALC880_ASUS_DIG2] = "asus-dig2", + [ALC880_UNIWILL_DIG] = "uniwill", + [ALC880_UNIWILL_P53] = "uniwill-p53", + [ALC880_FUJITSU] = "fujitsu", + [ALC880_F1734] = "F1734", + [ALC880_LG] = "lg", + [ALC880_LG_LW] = "lg-lw", + [ALC880_MEDION_RIM] = "medion", +#ifdef CONFIG_SND_DEBUG + [ALC880_TEST] = "test", +#endif + [ALC880_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc880_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), + SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), + SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), + SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), + SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V), + /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ + SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), + SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */ + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), + SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), + SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), + SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), + SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), + SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_F1734), + SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), + SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), + SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), + SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_F1734), + SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), + SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), + SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), + SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_LG), + SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), + SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), + /* default Intel */ + SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), + {} +}; + +/* + * ALC880 codec presets + */ +static const struct alc_config_preset alc880_presets[] = { + [ALC880_3ST] = { + .mixers = { alc880_three_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_3ST_DIG] = { + .mixers = { alc880_three_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_TCL_S700] = { + .mixers = { alc880_tcl_s700_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_tcl_S700_init_verbs, + alc880_gpio2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */ + .num_adc_nids = 1, /* single ADC */ + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_5ST] = { + .mixers = { alc880_three_stack_mixer, + alc880_five_stack_mixer}, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_5stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), + .channel_mode = alc880_fivestack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_5ST_DIG] = { + .mixers = { alc880_three_stack_mixer, + alc880_five_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_5stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), + .channel_mode = alc880_fivestack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_6ST] = { + .mixers = { alc880_six_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), + .dac_nids = alc880_6st_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), + .channel_mode = alc880_sixstack_modes, + .input_mux = &alc880_6stack_capture_source, + }, + [ALC880_6ST_DIG] = { + .mixers = { alc880_six_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), + .dac_nids = alc880_6st_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), + .channel_mode = alc880_sixstack_modes, + .input_mux = &alc880_6stack_capture_source, + }, + [ALC880_W810] = { + .mixers = { alc880_w810_base_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_w810_init_verbs, + alc880_gpio2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids), + .dac_nids = alc880_w810_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), + .channel_mode = alc880_w810_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_Z71V] = { + .mixers = { alc880_z71v_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_z71v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids), + .dac_nids = alc880_z71v_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_capture_source, + }, + [ALC880_F1734] = { + .mixers = { alc880_f1734_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_f1734_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids), + .dac_nids = alc880_f1734_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_f1734_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .setup = alc880_uniwill_p53_setup, + .init_hook = alc_hp_automute, + }, + [ALC880_ASUS] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_ASUS_DIG] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_ASUS_DIG2] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs, + alc880_gpio2_init_verbs }, /* use GPIO2 */ + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_ASUS_W1V] = { + .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_UNIWILL_DIG] = { + .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), + .channel_mode = alc880_asus_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_UNIWILL] = { + .mixers = { alc880_uniwill_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + .unsol_event = alc880_uniwill_unsol_event, + .setup = alc880_uniwill_setup, + .init_hook = alc880_uniwill_init_hook, + }, + [ALC880_UNIWILL_P53] = { + .mixers = { alc880_uniwill_p53_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_p53_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), + .dac_nids = alc880_asus_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), + .channel_mode = alc880_threestack_modes, + .input_mux = &alc880_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .setup = alc880_uniwill_p53_setup, + .init_hook = alc_hp_automute, + }, + [ALC880_FUJITSU] = { + .mixers = { alc880_fujitsu_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_p53_init_verbs, + alc880_beep_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .setup = alc880_uniwill_p53_setup, + .init_hook = alc_hp_automute, + }, + [ALC880_CLEVO] = { + .mixers = { alc880_three_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_clevo_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc880_capture_source, + }, + [ALC880_LG] = { + .mixers = { alc880_lg_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_lg_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids), + .dac_nids = alc880_lg_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes), + .channel_mode = alc880_lg_ch_modes, + .need_dac_fix = 1, + .input_mux = &alc880_lg_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc880_lg_setup, + .init_hook = alc_hp_automute, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .loopbacks = alc880_lg_loopbacks, +#endif + }, + [ALC880_LG_LW] = { + .mixers = { alc880_lg_lw_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_lg_lw_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes), + .channel_mode = alc880_lg_lw_modes, + .input_mux = &alc880_lg_lw_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc880_lg_lw_setup, + .init_hook = alc_hp_automute, + }, + [ALC880_MEDION_RIM] = { + .mixers = { alc880_medion_rim_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_medion_rim_init_verbs, + alc_gpio2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, + .input_mux = &alc880_medion_rim_capture_source, + .unsol_event = alc880_medion_rim_unsol_event, + .setup = alc880_medion_rim_setup, + .init_hook = alc880_medion_rim_automute, + }, +#ifdef CONFIG_SND_DEBUG + [ALC880_TEST] = { + .mixers = { alc880_test_mixer }, + .init_verbs = { alc880_test_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_test_dac_nids), + .dac_nids = alc880_test_dac_nids, + .dig_out_nid = ALC880_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_test_modes), + .channel_mode = alc880_test_modes, + .input_mux = &alc880_test_capture_source, + }, +#endif +}; + diff --git a/sound/pci/hda/alc882_quirks.c b/sound/pci/hda/alc882_quirks.c new file mode 100644 index 000000000000..617d04723b82 --- /dev/null +++ b/sound/pci/hda/alc882_quirks.c @@ -0,0 +1,3755 @@ +/* + * ALC882/ALC883/ALC888/ALC889 quirk models + * included by patch_realtek.c + */ + +/* ALC882 models */ +enum { + ALC882_AUTO, + ALC882_3ST_DIG, + ALC882_6ST_DIG, + ALC882_ARIMA, + ALC882_W2JC, + ALC882_TARGA, + ALC882_ASUS_A7J, + ALC882_ASUS_A7M, + ALC885_MACPRO, + ALC885_MBA21, + ALC885_MBP3, + ALC885_MB5, + ALC885_MACMINI3, + ALC885_IMAC24, + ALC885_IMAC91, + ALC883_3ST_2ch_DIG, + ALC883_3ST_6ch_DIG, + ALC883_3ST_6ch, + ALC883_6ST_DIG, + ALC883_TARGA_DIG, + ALC883_TARGA_2ch_DIG, + ALC883_TARGA_8ch_DIG, + ALC883_ACER, + ALC883_ACER_ASPIRE, + ALC888_ACER_ASPIRE_4930G, + ALC888_ACER_ASPIRE_6530G, + ALC888_ACER_ASPIRE_8930G, + ALC888_ACER_ASPIRE_7730G, + ALC883_MEDION, + ALC883_MEDION_WIM2160, + ALC883_LAPTOP_EAPD, + ALC883_LENOVO_101E_2ch, + ALC883_LENOVO_NB0763, + ALC888_LENOVO_MS7195_DIG, + ALC888_LENOVO_SKY, + ALC883_HAIER_W66, + ALC888_3ST_HP, + ALC888_6ST_DELL, + ALC883_MITAC, + ALC883_CLEVO_M540R, + ALC883_CLEVO_M720, + ALC883_FUJITSU_PI2515, + ALC888_FUJITSU_XA3530, + ALC883_3ST_6ch_INTEL, + ALC889A_INTEL, + ALC889_INTEL, + ALC888_ASUS_M90V, + ALC888_ASUS_EEE1601, + ALC889A_MB31, + ALC1200_ASUS_P5Q, + ALC883_SONY_VAIO_TT, + ALC882_MODEL_LAST, +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc888_4ST_ch2_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Line in */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc888_4ST_ch4_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc888_4ST_ch6_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc888_4ST_ch8_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Side */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +static const struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { + { 2, alc888_4ST_ch2_intel_init }, + { 4, alc888_4ST_ch4_intel_init }, + { 6, alc888_4ST_ch6_intel_init }, + { 8, alc888_4ST_ch8_intel_init }, +}; + +/* + * ALC888 Fujitsu Siemens Amillo xa3530 + */ + +static const struct hda_verb alc888_fujitsu_xa3530_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Connect Internal HP to Front */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect Bass HP to Front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect Line-Out side jack (SPDIF) to Side */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable unsolicited event for HP jack and Line-out jack */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {} +}; + +static void alc889_automute_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->autocfg.speaker_pins[3] = 0x19; + spec->autocfg.speaker_pins[4] = 0x1a; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc889_intel_init_hook(struct hda_codec *codec) +{ + alc889_coef_init(codec); + alc_hp_automute(codec); +} + +static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x17; /* line-out */ + spec->autocfg.hp_pins[1] = 0x1b; /* hp */ + spec->autocfg.speaker_pins[0] = 0x14; /* speaker */ + spec->autocfg.speaker_pins[1] = 0x15; /* bass */ + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* + * ALC888 Acer Aspire 4930G model + */ + +static const struct hda_verb alc888_acer_aspire_4930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal HP to front */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect HP out to front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + * ALC888 Acer Aspire 6530G model + */ + +static const struct hda_verb alc888_acer_aspire_6530g_verbs[] = { +/* Route to built-in subwoofer as well as speakers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +/* Bias voltage on for external mic port */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Enable speaker output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +/* Enable headphone output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + *ALC888 Acer Aspire 7730G model + */ + +static const struct hda_verb alc888_acer_aspire_7730G_verbs[] = { +/* Bias voltage on for external mic port */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Enable speaker output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +/* Enable headphone output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, +/*Enable internal subwoofer */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x17, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + * ALC889 Acer Aspire 8930G model + */ + +static const struct hda_verb alc889_acer_aspire_8930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal Front to Front */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect Internal Rear to Rear */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect Internal CLFE to CLFE */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect HP out to Front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable all DACs */ +/* DAC DISABLE/MUTE 1? */ +/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x03}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, +/* DAC DISABLE/MUTE 2? */ +/* some bit here disables the other DACs. Init=0x4900 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, +/* DMIC fix + * This laptop has a stereo digital microphone. The mics are only 1cm apart + * which makes the stereo useless. However, either the mic or the ALC889 + * makes the signal become a difference/sum signal instead of standard + * stereo, which is annoying. So instead we flip this bit which makes the + * codec replicate the sum signal to both channels, turning it into a + * normal mono mic. + */ +/* DMIC_CONTROL? Init value = 0x0001 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0003}, + { } +}; + +static const struct hda_input_mux alc888_2_capture_sources[2] = { + /* Front mic only available on one ADC */ + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + }, + }, + { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, + } +}; + +static const struct hda_input_mux alc888_acer_aspire_6530_sources[2] = { + /* Interal mic only available on one ADC */ + { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Line In", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + { "Internal Mic", 0xb }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line In", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + } +}; + +static const struct hda_input_mux alc889_capture_sources[3] = { + /* Digital mic only available on first "ADC" */ + { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + { "Input Mix", 0xa }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + } +}; + +static const struct snd_kcontrol_new alc888_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + +static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +#define ALC882_DIGOUT_NID 0x06 +#define ALC882_DIGIN_NID 0x0a +#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID +#define ALC883_DIGIN_NID ALC882_DIGIN_NID +#define ALC1200_DIGOUT_NID 0x10 + + +static const struct hda_channel_mode alc882_ch_modes[1] = { + { 8, NULL } +}; + +/* DACs */ +static const hda_nid_t alc882_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x03, 0x04, 0x05 +}; +#define alc883_dac_nids alc882_dac_nids + +/* ADCs */ +#define alc882_adc_nids alc880_adc_nids +#define alc882_adc_nids_alt alc880_adc_nids_alt +#define alc883_adc_nids alc882_adc_nids_alt +static const hda_nid_t alc883_adc_nids_alt[1] = { 0x08 }; +static const hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 }; +#define alc889_adc_nids alc880_adc_nids + +static const hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; +static const hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; +#define alc883_capsrc_nids alc882_capsrc_nids_alt +static const hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; +#define alc889_capsrc_nids alc882_capsrc_nids + +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ + +static const struct hda_input_mux alc882_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +#define alc883_capture_source alc882_capture_source + +static const struct hda_input_mux alc889_capture_source = { + .num_items = 3, + .items = { + { "Front Mic", 0x0 }, + { "Mic", 0x3 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux mb5_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x1 }, + { "Line", 0x7 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux macmini3_capture_source = { + .num_items = 2, + .items = { + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc883_3stack_6ch_intel = { + .num_items = 4, + .items = { + { "Mic", 0x1 }, + { "Front Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc883_lenovo_101e_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc883_lenovo_nb0763_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; + +static const struct hda_input_mux alc883_lenovo_sky_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x4 }, + }, +}; + +static const struct hda_input_mux alc883_asus_eee1601_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc889A_mb31_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + /* Front Mic (0x01) unused */ + { "Line", 0x2 }, + /* Line 2 (0x03) unused */ + /* CD (0x04) unused? */ + }, +}; + +static const struct hda_input_mux alc889A_imac91_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x01 }, + { "Line", 0x2 }, /* Not sure! */ + }, +}; + +/* + * 2ch mode + */ +static const struct hda_channel_mode alc883_3ST_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc882_3ST_ch2_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc882_3ST_ch4_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc882_3ST_ch6_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc882_3ST_6ch_modes[3] = { + { 2, alc882_3ST_ch2_init }, + { 4, alc882_3ST_ch4_init }, + { 6, alc882_3ST_ch6_init }, +}; + +#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes + +/* + * 2ch mode + */ +static const struct hda_verb alc883_3ST_ch2_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc883_3ST_ch4_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_3ST_ch6_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = { + { 2, alc883_3ST_ch2_clevo_init }, + { 4, alc883_3ST_ch4_clevo_init }, + { 6, alc883_3ST_ch6_clevo_init }, +}; + + +/* + * 6ch mode + */ +static const struct hda_verb alc882_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc882_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static const struct hda_channel_mode alc882_sixstack_modes[2] = { + { 6, alc882_sixstack_ch6_init }, + { 8, alc882_sixstack_ch8_init }, +}; + + +/* Macbook Air 2,1 */ + +static const struct hda_channel_mode alc885_mba21_ch_modes[1] = { + { 2, NULL }, +}; + +/* + * macbook pro ALC885 can switch LineIn to LineOut without losing Mic + */ + +/* + * 2ch mode + */ +static const struct hda_verb alc885_mbp_ch2_init[] = { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc885_mbp_ch4_init[] = { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } /* end */ +}; + +static const struct hda_channel_mode alc885_mbp_4ch_modes[2] = { + { 2, alc885_mbp_ch2_init }, + { 4, alc885_mbp_ch4_init }, +}; + +/* + * 2ch + * Speakers/Woofer/HP = Front + * LineIn = Input + */ +static const struct hda_verb alc885_mb5_ch2_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } /* end */ +}; + +/* + * 6ch mode + * Speakers/HP = Front + * Woofer = LFE + * LineIn = Surround + */ +static const struct hda_verb alc885_mb5_ch6_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + { } /* end */ +}; + +static const struct hda_channel_mode alc885_mb5_6ch_modes[2] = { + { 2, alc885_mb5_ch2_init }, + { 6, alc885_mb5_ch6_init }, +}; + +#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes + +/* + * 2ch mode + */ +static const struct hda_verb alc883_4ST_ch2_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc883_4ST_ch4_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_4ST_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc883_4ST_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_4ST_8ch_modes[4] = { + { 2, alc883_4ST_ch2_init }, + { 4, alc883_4ST_ch4_init }, + { 6, alc883_4ST_ch6_init }, + { 8, alc883_4ST_ch8_init }, +}; + + +/* + * 2ch mode + */ +static const struct hda_verb alc883_3ST_ch2_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc883_3ST_ch4_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_3ST_ch6_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { + { 2, alc883_3ST_ch2_intel_init }, + { 4, alc883_3ST_ch4_intel_init }, + { 6, alc883_3ST_ch6_intel_init }, +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc889_ch2_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc889_ch6_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc889_ch8_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } /* end */ +}; + +static const struct hda_channel_mode alc889_8ch_intel_modes[3] = { + { 2, alc889_ch2_intel_init }, + { 6, alc889_ch6_intel_init }, + { 8, alc889_ch8_intel_init }, +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc883_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_sixstack_modes[2] = { + { 6, alc883_sixstack_ch6_init }, + { 8, alc883_sixstack_ch8_init }, +}; + + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ +static const struct snd_kcontrol_new alc882_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +/* Macbook Air 2,1 same control for HP and internal Speaker */ + +static const struct snd_kcontrol_new alc885_mba21_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT), + { } +}; + + +static const struct snd_kcontrol_new alc885_mbp3_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, 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, 0x00, HDA_INPUT), + HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc885_mb5_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0x00, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc885_macmini3_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc885_imac91_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), + { } /* end */ +}; + + +static const struct snd_kcontrol_new alc882_w2jc_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc882_targa_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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 Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ??? + * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c + */ +static const struct snd_kcontrol_new alc882_asus_a7j_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, 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 Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc882_asus_a7m_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc882_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static const struct hda_verb alc882_base_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* CLFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Side mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + { } +}; + +static const struct hda_verb alc882_adc1_init_verbs[] = { + /* 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(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } +}; + +static const struct hda_verb alc882_eapd_verbs[] = { + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + { } +}; + +static const struct hda_verb alc889_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc_hp15_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc885_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* CLFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Side mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Front HP Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Rear Pin: output 1 (0x0d) */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic (rear) pin: input vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* Mixer elements: 0x18, , 0x1a, 0x1b */ + /* Input mixer1 */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + + { } +}; + +static const struct hda_verb alc885_init_input_verbs[] = { + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + { } +}; + + +/* Unmute Selector 24h and set the default input to front mic */ +static const struct hda_verb alc889_init_input_verbs[] = { + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + { } +}; + + +#define alc883_init_verbs alc882_base_init_verbs + +/* Mac Pro test */ +static const struct snd_kcontrol_new alc882_macpro_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + /* FIXME: this looks suspicious... + HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT), + */ + { } /* end */ +}; + +static const struct hda_verb alc882_macpro_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Speaker: output */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04}, + /* Headphone output (output 0 - 0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 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(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + { } +}; + +/* Macbook 5,1 */ +static const struct hda_verb alc885_mb5_init_verbs[] = { + /* DACs */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Front mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Surround mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* LFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* HP mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* LFE Pin (0x0e) */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* HP Pin (0x0f) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0x1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x7)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x4)}, + { } +}; + +/* Macmini 3,1 */ +static const struct hda_verb alc885_macmini3_init_verbs[] = { + /* DACs */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Front mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Surround mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* LFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* HP mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* LFE Pin (0x0e) */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* HP Pin (0x0f) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* Line In pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + { } +}; + + +static const struct hda_verb alc885_mba21_init_verbs[] = { + /*Internal and HP Speaker Mixer*/ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /*Internal Speaker Pin (0x0c)*/ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP Pin: output 0 (0x0e) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC_HP_EVENT | AC_USRSP_EN)}, + /* Line in (is hp when jack connected)*/ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + { } + }; + + +/* Macbook Pro rev3 */ +static const struct hda_verb alc885_mbp3_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* HP mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP Pin: output 0 (0x0e) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: use output 1 when in LineOut mode */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 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(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + { } +}; + +/* iMac 9,1 */ +static const struct hda_verb alc885_imac91_init_verbs[] = { + /* Internal Speaker Pin (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP Pin: Rear */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC_HP_EVENT | AC_USRSP_EN)}, + /* Line in Rear */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* 0x24 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* 0x23 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* 0x22 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* 0x07 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* 0x08 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* 0x09 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } +}; + +/* iMac 24 mixer. */ +static const struct snd_kcontrol_new alc885_imac24_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT), + { } /* end */ +}; + +/* iMac 24 init verbs. */ +static const struct hda_verb alc885_imac24_init_verbs[] = { + /* Internal speakers: output 0 (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Internal speakers: output 0 (0x0c) */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Headphone: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* Front Mic: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; + +/* Toggle speaker-output according to the hp-jack state */ +static void alc885_imac24_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x18; + spec->autocfg.speaker_pins[1] = 0x1a; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +#define alc885_mb5_setup alc885_imac24_setup +#define alc885_macmini3_setup alc885_imac24_setup + +/* Macbook Air 2,1 */ +static void alc885_mba21_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x18; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + + + +static void alc885_mbp3_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc885_imac91_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x18; + spec->autocfg.speaker_pins[1] = 0x1a; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc882_targa_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc882_targa_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_hp_automute(codec); + snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, + spec->jack_present ? 1 : 3); +} + +static void alc882_targa_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc882_targa_automute(codec); +} + +static const struct hda_verb alc882_asus_a7j_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + { } /* end */ +}; + +static const struct hda_verb alc882_asus_a7m_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + { } /* end */ +}; + +static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) +{ + unsigned int gpiostate, gpiomask, gpiodir; + + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + + if (!muted) + gpiostate |= (1 << pin); + else + gpiostate &= ~(1 << pin); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= (1 << pin); + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= (1 << pin); + + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, gpiomask); + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); + + msleep(1); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); +} + +/* set up GPIO at initialization */ +static void alc885_macpro_init_hook(struct hda_codec *codec) +{ + alc882_gpio_mute(codec, 0, 0); + alc882_gpio_mute(codec, 1, 0); +} + +/* set up GPIO and update auto-muting at initialization */ +static void alc885_imac24_init_hook(struct hda_codec *codec) +{ + alc885_macpro_init_hook(codec); + alc_hp_automute(codec); +} + +/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */ +static const struct hda_verb alc889A_mb31_ch2_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ + { } /* end */ +}; + +/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */ +static const struct hda_verb alc889A_mb31_ch4_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ + { } /* end */ +}; + +/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */ +static const struct hda_verb alc889A_mb31_ch5_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ + { } /* end */ +}; + +/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */ +static const struct hda_verb alc889A_mb31_ch6_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ + { } /* end */ +}; + +static const struct hda_channel_mode alc889A_mb31_6ch_modes[4] = { + { 2, alc889A_mb31_ch2_init }, + { 4, alc889A_mb31_ch4_init }, + { 5, alc889A_mb31_ch5_init }, + { 6, alc889A_mb31_ch6_init }, +}; + +static const struct hda_verb alc883_medion_eapd_verbs[] = { + /* eanable EAPD on medion laptop */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + { } +}; + +#define alc883_base_mixer alc882_base_mixer + +static const struct snd_kcontrol_new alc883_mitac_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc885_8ch_intel_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x1b, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_fivestack_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_targa_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_targa_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_targa_8ch_mixer[] = { + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_medion_wim2160_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc883_medion_wim2160_verbs[] = { + /* Unmute front mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Set speaker pin to front mixer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Init headphone pin */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_medion_wim2160_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1a; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", + 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc889A_mb31_mixer[] = { + /* Output mixers */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT), + /* Output switches */ + HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT), + /* Boost mixers */ + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT), + /* Input mixers */ + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_vaiott_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc883_bind_cap_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc883_bind_cap_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = { + HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), + HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_mitac_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc883_mitac_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, */ + + { } /* end */ +}; + +static const struct hda_verb alc883_clevo_m540r_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/ + + /* enable unsolicited event */ + /* + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + */ + + { } /* end */ +}; + +static const struct hda_verb alc883_clevo_m720_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static const struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { + /* HP */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static const struct hda_verb alc883_targa_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + +/* Connect Line-Out side jack (SPDIF) to Side */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static const struct hda_verb alc883_lenovo_101e_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_FRONT_EVENT|AC_USRSP_EN}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT|AC_USRSP_EN}, + { } /* end */ +}; + +static const struct hda_verb alc883_lenovo_nb0763_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + { } /* end */ +}; + +static const struct hda_verb alc888_lenovo_ms7195_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_FRONT_EVENT | AC_USRSP_EN}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static const struct hda_verb alc883_haier_w66_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + { } /* end */ +}; + +static const struct hda_verb alc888_lenovo_sky_verbs[] = { + {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)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static const struct hda_verb alc888_6st_dell_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static const struct hda_verb alc883_vaiott_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static void alc888_3st_hp_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x18; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc888_3st_hp_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc888_3st_hp_2ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc888_3st_hp_4ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc888_3st_hp_6ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc888_3st_hp_modes[3] = { + { 2, alc888_3st_hp_2ch_init }, + { 4, alc888_3st_hp_4ch_init }, + { 6, alc888_3st_hp_6ch_init }, +}; + +static void alc888_lenovo_ms7195_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.line_out_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_lenovo_nb0763_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* toggle speaker-output according to the hp-jack state */ +#define alc883_targa_init_hook alc882_targa_init_hook +#define alc883_targa_unsol_event alc882_targa_unsol_event + +static void alc883_clevo_m720_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_clevo_m720_init_hook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc88x_simple_mic_automute(codec); +} + +static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC_MIC_EVENT: + alc88x_simple_mic_automute(codec); + break; + default: + alc_sku_unsol_event(codec, res); + break; + } +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_haier_w66_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_lenovo_101e_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.line_out_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->detect_line = 1; + spec->automute_lines = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_acer_aspire_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc883_acer_eapd_verbs[] = { + /* HP Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* eanable EAPD on medion laptop */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static void alc888_6st_dell_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->autocfg.speaker_pins[3] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc888_lenovo_sky_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->autocfg.speaker_pins[3] = 0x17; + spec->autocfg.speaker_pins[4] = 0x1a; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_vaiott_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc888_asus_m90v_verbs[] = { + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* enable unsolicited event */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static void alc883_mode2_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc888_asus_eee1601_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0838}, + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static void alc883_eee1601_inithook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + alc_hp_automute(codec); +} + +static const struct hda_verb alc889A_mb31_verbs[] = { + /* Init rear pin (used as headphone output) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* Init line pin (used as output in 4ch and 6ch mode) */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */ + /* Init line 2 pin (used as headphone out by default) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */ + { } /* end */ +}; + +/* Mute speakers according to the headphone jack state */ +static void alc889A_mb31_automute(struct hda_codec *codec) +{ + unsigned int present; + + /* Mute only in 2ch or 4ch mode */ + if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0) + == 0x00) { + present = snd_hda_jack_detect(codec, 0x15); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + } +} + +static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc889A_mb31_automute(codec); +} + +static const hda_nid_t alc883_slave_dig_outs[] = { + ALC1200_DIGOUT_NID, 0, +}; + +static const hda_nid_t alc1200_slave_dig_outs[] = { + ALC883_DIGOUT_NID, 0, +}; + +/* + * configuration and preset + */ +static const char * const alc882_models[ALC882_MODEL_LAST] = { + [ALC882_3ST_DIG] = "3stack-dig", + [ALC882_6ST_DIG] = "6stack-dig", + [ALC882_ARIMA] = "arima", + [ALC882_W2JC] = "w2jc", + [ALC882_TARGA] = "targa", + [ALC882_ASUS_A7J] = "asus-a7j", + [ALC882_ASUS_A7M] = "asus-a7m", + [ALC885_MACPRO] = "macpro", + [ALC885_MB5] = "mb5", + [ALC885_MACMINI3] = "macmini3", + [ALC885_MBA21] = "mba21", + [ALC885_MBP3] = "mbp3", + [ALC885_IMAC24] = "imac24", + [ALC885_IMAC91] = "imac91", + [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig", + [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", + [ALC883_3ST_6ch] = "3stack-6ch", + [ALC883_6ST_DIG] = "alc883-6stack-dig", + [ALC883_TARGA_DIG] = "targa-dig", + [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", + [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig", + [ALC883_ACER] = "acer", + [ALC883_ACER_ASPIRE] = "acer-aspire", + [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", + [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g", + [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g", + [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g", + [ALC883_MEDION] = "medion", + [ALC883_MEDION_WIM2160] = "medion-wim2160", + [ALC883_LAPTOP_EAPD] = "laptop-eapd", + [ALC883_LENOVO_101E_2ch] = "lenovo-101e", + [ALC883_LENOVO_NB0763] = "lenovo-nb0763", + [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", + [ALC888_LENOVO_SKY] = "lenovo-sky", + [ALC883_HAIER_W66] = "haier-w66", + [ALC888_3ST_HP] = "3stack-hp", + [ALC888_6ST_DELL] = "6stack-dell", + [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M540R] = "clevo-m540r", + [ALC883_CLEVO_M720] = "clevo-m720", + [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", + [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", + [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", + [ALC889A_INTEL] = "intel-alc889a", + [ALC889_INTEL] = "intel-x58", + [ALC1200_ASUS_P5Q] = "asus-p5q", + [ALC889A_MB31] = "mb31", + [ALC883_SONY_VAIO_TT] = "sony-vaio-tt", + [ALC882_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc882_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), + + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", + ALC888_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", + ALC888_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", + ALC888_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", + ALC888_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO), + SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO), + SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", + ALC888_ACER_ASPIRE_6530G), + SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", + ALC888_ACER_ASPIRE_6530G), + SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", + ALC888_ACER_ASPIRE_7730G), + /* default Acer -- disabled as it causes more problems. + * model=auto should work fine now + */ + /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */ + + SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), + + SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavilion", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP), + + SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), + SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), + SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), + SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), + SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), + SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), + SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), + + SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), + SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), + SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), + + SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ + SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), + + SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), + /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */ + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), + SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx", + ALC883_FUJITSU_PI2515), + SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx", + ALC888_FUJITSU_XA3530), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), + SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY), + SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG), + SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), + + SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), + SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL), + SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL), + SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG), + + {} +}; + +/* codec SSID table for Intel Mac */ +static const struct snd_pci_quirk alc882_ssid_cfg_tbl[] = { + SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO), + SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31), + SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M), + SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC885_MBA21), + SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31), + SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91), + SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5), + SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC885_MB5), + /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2, + * so apparently no perfect solution yet + */ + SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5), + SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5), + SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3), + {} /* terminator */ +}; + +static const struct alc_config_preset alc882_presets[] = { + [ALC882_3ST_DIG] = { + .mixers = { alc882_base_mixer }, + .init_verbs = { alc882_base_init_verbs, + alc882_adc1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC882_6ST_DIG] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, + alc882_adc1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, + [ALC882_ARIMA] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, + [ALC882_W2JC] = { + .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs, alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + }, + [ALC885_MBA21] = { + .mixers = { alc885_mba21_mixer }, + .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs }, + .num_dacs = 2, + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mba21_ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), + .input_mux = &alc882_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_mba21_setup, + .init_hook = alc_hp_automute, + }, + [ALC885_MBP3] = { + .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_mbp3_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = 2, + .dac_nids = alc882_dac_nids, + .hp_nid = 0x04, + .channel_mode = alc885_mbp_4ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes), + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_mbp3_setup, + .init_hook = alc_hp_automute, + }, + [ALC885_MB5] = { + .mixers = { alc885_mb5_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_mb5_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mb5_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes), + .input_mux = &mb5_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_mb5_setup, + .init_hook = alc_hp_automute, + }, + [ALC885_MACMINI3] = { + .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_macmini3_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_macmini3_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes), + .input_mux = &macmini3_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_macmini3_setup, + .init_hook = alc_hp_automute, + }, + [ALC885_MACPRO] = { + .mixers = { alc882_macpro_mixer }, + .init_verbs = { alc882_macpro_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .init_hook = alc885_macpro_init_hook, + }, + [ALC885_IMAC24] = { + .mixers = { alc885_imac24_mixer }, + .init_verbs = { alc885_imac24_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_imac24_setup, + .init_hook = alc885_imac24_init_hook, + }, + [ALC885_IMAC91] = { + .mixers = {alc885_imac91_mixer}, + .init_verbs = { alc885_imac91_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mba21_ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), + .input_mux = &alc889A_imac91_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_imac91_setup, + .init_hook = alc_hp_automute, + }, + [ALC882_TARGA] = { + .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc880_gpio3_init_verbs, alc882_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), + .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), + .channel_mode = alc882_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC882_ASUS_A7J] = { + .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_asus_a7j_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), + .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), + .channel_mode = alc882_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC882_ASUS_A7M] = { + .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs, alc880_gpio1_init_verbs, + alc882_asus_a7m_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC883_3ST_2ch_DIG] = { + .mixers = { alc883_3ST_2ch_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch_DIG] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch_INTEL] = { + .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes), + .channel_mode = alc883_3ST_6ch_intel_modes, + .need_dac_fix = 1, + .input_mux = &alc883_3stack_6ch_intel, + }, + [ALC889A_INTEL] = { + .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc885_init_verbs, alc885_init_input_verbs, + alc_hp15_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), + .channel_mode = alc889_8ch_intel_modes, + .capsrc_nids = alc889_capsrc_nids, + .input_mux = &alc889_capture_source, + .setup = alc889_automute_setup, + .init_hook = alc_hp_automute, + .unsol_event = alc_sku_unsol_event, + .need_dac_fix = 1, + }, + [ALC889_INTEL] = { + .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc885_init_verbs, alc889_init_input_verbs, + alc889_eapd_verbs, alc_hp15_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), + .channel_mode = alc889_8ch_intel_modes, + .capsrc_nids = alc889_capsrc_nids, + .input_mux = &alc889_capture_source, + .setup = alc889_automute_setup, + .init_hook = alc889_intel_init_hook, + .unsol_event = alc_sku_unsol_event, + .need_dac_fix = 1, + }, + [ALC883_6ST_DIG] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_TARGA_DIG] = { + .mixers = { alc883_targa_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC883_TARGA_2ch_DIG] = { + .mixers = { alc883_targa_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC883_TARGA_8ch_DIG] = { + .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_targa_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes), + .channel_mode = alc883_4ST_8ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC883_ACER] = { + .mixers = { alc883_base_mixer }, + /* On TravelMate laptops, GPIO 0 enables the internal speaker + * and the headphone jack. Turn this on and rely on the + * standard mute methods whenever the user wants to turn + * these outputs off. + */ + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_ACER_ASPIRE] = { + .mixers = { alc883_acer_aspire_mixer }, + .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_acer_aspire_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ACER_ASPIRE_4930G] = { + .mixers = { alc888_acer_aspire_4930g_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_4930g_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_acer_aspire_4930g_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ACER_ASPIRE_6530G] = { + .mixers = { alc888_acer_aspire_6530_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_6530g_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_acer_aspire_6530_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_acer_aspire_6530g_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ACER_ASPIRE_8930G] = { + .mixers = { alc889_acer_aspire_8930g_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc889_acer_aspire_8930g_verbs, + alc889_eapd_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .capsrc_nids = alc889_capsrc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .num_mux_defs = + ARRAY_SIZE(alc889_capture_sources), + .input_mux = alc889_capture_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc889_acer_aspire_8930g_setup, + .init_hook = alc_hp_automute, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .power_hook = alc_power_eapd, +#endif + }, + [ALC888_ACER_ASPIRE_7730G] = { + .mixers = { alc883_3ST_6ch_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_7730G_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_acer_aspire_7730g_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_MEDION] = { + .mixers = { alc883_fivestack_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_medion_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_MEDION_WIM2160] = { + .mixers = { alc883_medion_wim2160_mixer }, + .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_medion_wim2160_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_LAPTOP_EAPD] = { + .mixers = { alc883_base_mixer }, + .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_CLEVO_M540R] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes), + .channel_mode = alc883_3ST_6ch_clevo_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + /* This machine has the hardware HP auto-muting, thus + * we need no software mute via unsol event + */ + }, + [ALC883_CLEVO_M720] = { + .mixers = { alc883_clevo_m720_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_clevo_m720_unsol_event, + .setup = alc883_clevo_m720_setup, + .init_hook = alc883_clevo_m720_init_hook, + }, + [ALC883_LENOVO_101E_2ch] = { + .mixers = { alc883_lenovo_101e_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_lenovo_101e_capture_source, + .setup = alc883_lenovo_101e_setup, + .unsol_event = alc_sku_unsol_event, + .init_hook = alc_inithook, + }, + [ALC883_LENOVO_NB0763] = { + .mixers = { alc883_lenovo_nb0763_mixer }, + .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_lenovo_nb0763_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_lenovo_nb0763_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_LENOVO_MS7195_DIG] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_lenovo_ms7195_setup, + .init_hook = alc_inithook, + }, + [ALC883_HAIER_W66] = { + .mixers = { alc883_targa_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_haier_w66_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_3ST_HP] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), + .channel_mode = alc888_3st_hp_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_3st_hp_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_6ST_DELL] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_6st_dell_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_MITAC] = { + .mixers = { alc883_mitac_mixer }, + .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_mitac_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_FUJITSU_PI2515] = { + .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_2ch_fujitsu_pi2515_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_2ch_fujitsu_pi2515_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_FUJITSU_XA3530] = { + .mixers = { alc888_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, + alc888_fujitsu_xa3530_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes), + .channel_mode = alc888_4ST_8ch_intel_modes, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_fujitsu_xa3530_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_LENOVO_SKY] = { + .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .need_dac_fix = 1, + .input_mux = &alc883_lenovo_sky_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_lenovo_sky_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ASUS_M90V] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_mode2_setup, + .init_hook = alc_inithook, + }, + [ALC888_ASUS_EEE1601] = { + .mixers = { alc883_asus_eee1601_mixer }, + .cap_mixer = alc883_asus_eee1601_cap_mixer, + .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_asus_eee1601_capture_source, + .unsol_event = alc_sku_unsol_event, + .init_hook = alc883_eee1601_inithook, + }, + [ALC1200_ASUS_P5Q] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC1200_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc1200_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC889A_MB31] = { + .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer}, + .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs, + alc880_gpio1_init_verbs }, + .adc_nids = alc883_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .capsrc_nids = alc883_capsrc_nids, + .dac_nids = alc883_dac_nids, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .channel_mode = alc889A_mb31_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes), + .input_mux = &alc889A_mb31_capture_source, + .dig_out_nid = ALC883_DIGOUT_NID, + .unsol_event = alc889A_mb31_unsol_event, + .init_hook = alc889A_mb31_automute, + }, + [ALC883_SONY_VAIO_TT] = { + .mixers = { alc883_vaiott_mixer }, + .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_vaiott_setup, + .init_hook = alc_hp_automute, + }, +}; + + diff --git a/sound/pci/hda/alc_quirks.c b/sound/pci/hda/alc_quirks.c new file mode 100644 index 000000000000..2be1129cf458 --- /dev/null +++ b/sound/pci/hda/alc_quirks.c @@ -0,0 +1,467 @@ +/* + * Common codes for Realtek codec quirks + * included by patch_realtek.c + */ + +/* + * configuration template - to be copied to the spec instance + */ +struct alc_config_preset { + const struct snd_kcontrol_new *mixers[5]; /* should be identical size + * with spec + */ + const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ + const struct hda_verb *init_verbs[5]; + unsigned int num_dacs; + const hda_nid_t *dac_nids; + hda_nid_t dig_out_nid; /* optional */ + hda_nid_t hp_nid; /* optional */ + const hda_nid_t *slave_dig_outs; + unsigned int num_adc_nids; + const hda_nid_t *adc_nids; + const hda_nid_t *capsrc_nids; + hda_nid_t dig_in_nid; + unsigned int num_channel_mode; + const struct hda_channel_mode *channel_mode; + int need_dac_fix; + int const_channel_count; + unsigned int num_mux_defs; + const struct hda_input_mux *input_mux; + void (*unsol_event)(struct hda_codec *, unsigned int); + void (*setup)(struct hda_codec *); + void (*init_hook)(struct hda_codec *); +#ifdef CONFIG_SND_HDA_POWER_SAVE + const struct hda_amp_list *loopbacks; + void (*power_hook)(struct hda_codec *codec); +#endif +}; + +/* + * channel mode setting + */ +static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, + spec->num_channel_mode); +} + +static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, + spec->num_channel_mode, + spec->ext_channel_count); +} + +static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, + spec->num_channel_mode, + &spec->ext_channel_count); + if (err >= 0 && !spec->const_channel_count) { + spec->multiout.max_channels = spec->ext_channel_count; + if (spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + } + return err; +} + +/* + * Control the mode of pin widget settings via the mixer. "pc" is used + * instead of "%" to avoid consequences of accidentally treating the % as + * being part of a format specifier. Maximum allowed length of a value is + * 63 characters plus NULL terminator. + * + * Note: some retasking pin complexes seem to ignore requests for input + * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these + * are requested. Therefore order this list so that this behaviour will not + * cause problems when mixer clients move through the enum sequentially. + * NIDs 0x0f and 0x10 have been observed to have this behaviour as of + * March 2006. + */ +static const char * const alc_pin_mode_names[] = { + "Mic 50pc bias", "Mic 80pc bias", + "Line in", "Line out", "Headphone out", +}; +static const unsigned char alc_pin_mode_values[] = { + PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP, +}; +/* The control can present all 5 options, or it can limit the options based + * in the pin being assumed to be exclusively an input or an output pin. In + * addition, "input" pins may or may not process the mic bias option + * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to + * accept requests for bias as of chip versions up to March 2006) and/or + * wiring in the computer. + */ +#define ALC_PIN_DIR_IN 0x00 +#define ALC_PIN_DIR_OUT 0x01 +#define ALC_PIN_DIR_INOUT 0x02 +#define ALC_PIN_DIR_IN_NOMICBIAS 0x03 +#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04 + +/* Info about the pin modes supported by the different pin direction modes. + * For each direction the minimum and maximum values are given. + */ +static const signed char alc_pin_mode_dir_info[5][2] = { + { 0, 2 }, /* ALC_PIN_DIR_IN */ + { 3, 4 }, /* ALC_PIN_DIR_OUT */ + { 0, 4 }, /* ALC_PIN_DIR_INOUT */ + { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */ + { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */ +}; +#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0]) +#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1]) +#define alc_pin_mode_n_items(_dir) \ + (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1) + +static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int item_num = uinfo->value.enumerated.item; + unsigned char dir = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = alc_pin_mode_n_items(dir); + + if (item_numalc_pin_mode_max(dir)) + item_num = alc_pin_mode_min(dir); + strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]); + return 0; +} + +static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int i; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char dir = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); + + /* Find enumerated value for current pinctl setting */ + i = alc_pin_mode_min(dir); + while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl) + i++; + *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); + return 0; +} + +static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + signed int change; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char dir = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); + + if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) + val = alc_pin_mode_min(dir); + + change = pinctl != alc_pin_mode_values[val]; + if (change) { + /* Set pin mode to that requested */ + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + alc_pin_mode_values[val]); + + /* Also enable the retasking pin's input/output as required + * for the requested pin mode. Enum values of 2 or less are + * input modes. + * + * Dynamically switching the input/output buffers probably + * reduces noise slightly (particularly on input) so we'll + * do it. However, having both input and output buffers + * enabled simultaneously doesn't seem to be problematic if + * this turns out to be necessary in the future. + */ + if (val <= 2) { + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, + HDA_AMP_MUTE, 0); + } else { + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); + } + } + return change; +} + +#define ALC_PIN_MODE(xname, nid, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ + .info = alc_pin_mode_info, \ + .get = alc_pin_mode_get, \ + .put = alc_pin_mode_put, \ + .private_value = nid | (dir<<16) } + +/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged + * together using a mask with more than one bit set. This control is + * currently used only by the ALC260 test model. At this stage they are not + * needed for any "production" models. + */ +#ifdef CONFIG_SND_DEBUG +#define alc_gpio_data_info snd_ctl_boolean_mono_info + +static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0x00); + + *valp = (val & mask) != 0; + return 0; +} +static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + signed int change; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, + 0x00); + + /* Set/unset the masked GPIO bit(s) as needed */ + change = (val == 0 ? 0 : mask) != (gpio_data & mask); + if (val == 0) + gpio_data &= ~mask; + else + gpio_data |= mask; + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_GPIO_DATA, gpio_data); + + return change; +} +#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ + .info = alc_gpio_data_info, \ + .get = alc_gpio_data_get, \ + .put = alc_gpio_data_put, \ + .private_value = nid | (mask<<16) } +#endif /* CONFIG_SND_DEBUG */ + +/* A switch control to allow the enabling of the digital IO pins on the + * ALC260. This is incredibly simplistic; the intention of this control is + * to provide something in the test model allowing digital outputs to be + * identified if present. If models are found which can utilise these + * outputs a more complete mixer control can be devised for those models if + * necessary. + */ +#ifdef CONFIG_SND_DEBUG +#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info + +static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0x00); + + *valp = (val & mask) != 0; + return 0; +} +static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + signed int change; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, + 0x00); + + /* Set/unset the masked control bit(s) as needed */ + change = (val == 0 ? 0 : mask) != (ctrl_data & mask); + if (val==0) + ctrl_data &= ~mask; + else + ctrl_data |= mask; + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, + ctrl_data); + + return change; +} +#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ + .info = alc_spdif_ctrl_info, \ + .get = alc_spdif_ctrl_get, \ + .put = alc_spdif_ctrl_put, \ + .private_value = nid | (mask<<16) } +#endif /* CONFIG_SND_DEBUG */ + +/* A switch control to allow the enabling EAPD digital outputs on the ALC26x. + * Again, this is only used in the ALC26x test models to help identify when + * the EAPD line must be asserted for features to work. + */ +#ifdef CONFIG_SND_DEBUG +#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info + +static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0x00); + + *valp = (val & mask) != 0; + return 0; +} + +static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, + 0x00); + + /* Set/unset the masked control bit(s) as needed */ + change = (!val ? 0 : mask) != (ctrl_data & mask); + if (!val) + ctrl_data &= ~mask; + else + ctrl_data |= mask; + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + ctrl_data); + + return change; +} + +#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ + .info = alc_eapd_ctrl_info, \ + .get = alc_eapd_ctrl_get, \ + .put = alc_eapd_ctrl_put, \ + .private_value = nid | (mask<<16) } +#endif /* CONFIG_SND_DEBUG */ + +static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (!cfg->line_outs) { + while (cfg->line_outs < AUTO_CFG_MAX_OUTS && + cfg->line_out_pins[cfg->line_outs]) + cfg->line_outs++; + } + if (!cfg->speaker_outs) { + while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS && + cfg->speaker_pins[cfg->speaker_outs]) + cfg->speaker_outs++; + } + if (!cfg->hp_outs) { + while (cfg->hp_outs < AUTO_CFG_MAX_OUTS && + cfg->hp_pins[cfg->hp_outs]) + cfg->hp_outs++; + } +} + +/* + * set up from the preset table + */ +static void setup_preset(struct hda_codec *codec, + const struct alc_config_preset *preset) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) + add_mixer(spec, preset->mixers[i]); + spec->cap_mixer = preset->cap_mixer; + for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; + i++) + add_verb(spec, preset->init_verbs[i]); + + spec->channel_mode = preset->channel_mode; + spec->num_channel_mode = preset->num_channel_mode; + spec->need_dac_fix = preset->need_dac_fix; + spec->const_channel_count = preset->const_channel_count; + + if (preset->const_channel_count) + spec->multiout.max_channels = preset->const_channel_count; + else + spec->multiout.max_channels = spec->channel_mode[0].channels; + spec->ext_channel_count = spec->channel_mode[0].channels; + + spec->multiout.num_dacs = preset->num_dacs; + spec->multiout.dac_nids = preset->dac_nids; + spec->multiout.dig_out_nid = preset->dig_out_nid; + spec->multiout.slave_dig_outs = preset->slave_dig_outs; + spec->multiout.hp_nid = preset->hp_nid; + + spec->num_mux_defs = preset->num_mux_defs; + if (!spec->num_mux_defs) + spec->num_mux_defs = 1; + spec->input_mux = preset->input_mux; + + spec->num_adc_nids = preset->num_adc_nids; + spec->adc_nids = preset->adc_nids; + spec->capsrc_nids = preset->capsrc_nids; + spec->dig_in_nid = preset->dig_in_nid; + + spec->unsol_event = preset->unsol_event; + spec->init_hook = preset->init_hook; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->power_hook = preset->power_hook; + spec->loopback.amplist = preset->loopbacks; +#endif + + if (preset->setup) + preset->setup(codec); + + alc_fixup_autocfg_pin_nums(codec); +} + + +/* auto-toggle front mic */ +static void alc88x_simple_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_jack_detect(codec, 0x18); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); +} + diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8366e02df3cc..c1adb3bce7e8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1,7 +1,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for ALC 260/880/882 codecs + * HD audio interface patch for Realtek ALC codecs * * Copyright (c) 2004 Kailang Yang * PeiSen Hou @@ -33,236 +33,11 @@ #include "hda_local.h" #include "hda_beep.h" -#define ALC880_FRONT_EVENT 0x01 -#define ALC880_DCVOL_EVENT 0x02 -#define ALC880_HP_EVENT 0x04 -#define ALC880_MIC_EVENT 0x08 - -/* ALC880 board config type */ -enum { - ALC880_3ST, - ALC880_3ST_DIG, - ALC880_5ST, - ALC880_5ST_DIG, - ALC880_W810, - ALC880_Z71V, - ALC880_6ST, - ALC880_6ST_DIG, - ALC880_F1734, - ALC880_ASUS, - ALC880_ASUS_DIG, - ALC880_ASUS_W1V, - ALC880_ASUS_DIG2, - ALC880_FUJITSU, - ALC880_UNIWILL_DIG, - ALC880_UNIWILL, - ALC880_UNIWILL_P53, - ALC880_CLEVO, - ALC880_TCL_S700, - ALC880_LG, - ALC880_LG_LW, - ALC880_MEDION_RIM, -#ifdef CONFIG_SND_DEBUG - ALC880_TEST, -#endif - ALC880_AUTO, - ALC880_MODEL_LAST /* last tag */ -}; - -/* ALC260 models */ -enum { - ALC260_BASIC, - ALC260_HP, - ALC260_HP_DC7600, - ALC260_HP_3013, - ALC260_FUJITSU_S702X, - ALC260_ACER, - ALC260_WILL, - ALC260_REPLACER_672V, - ALC260_FAVORIT100, -#ifdef CONFIG_SND_DEBUG - ALC260_TEST, -#endif - ALC260_AUTO, - ALC260_MODEL_LAST /* last tag */ -}; - -/* ALC262 models */ -enum { - ALC262_BASIC, - ALC262_HIPPO, - ALC262_HIPPO_1, - ALC262_FUJITSU, - ALC262_HP_BPC, - ALC262_HP_BPC_D7000_WL, - ALC262_HP_BPC_D7000_WF, - ALC262_HP_TC_T5735, - ALC262_HP_RP5700, - ALC262_BENQ_ED8, - ALC262_SONY_ASSAMD, - ALC262_BENQ_T31, - ALC262_ULTRA, - ALC262_LENOVO_3000, - ALC262_NEC, - ALC262_TOSHIBA_S06, - ALC262_TOSHIBA_RX1, - ALC262_TYAN, - ALC262_AUTO, - ALC262_MODEL_LAST /* last tag */ -}; - -/* ALC268 models */ -enum { - ALC267_QUANTA_IL1, - ALC268_3ST, - ALC268_TOSHIBA, - ALC268_ACER, - ALC268_ACER_DMIC, - ALC268_ACER_ASPIRE_ONE, - ALC268_DELL, - ALC268_ZEPTO, -#ifdef CONFIG_SND_DEBUG - ALC268_TEST, -#endif - ALC268_AUTO, - ALC268_MODEL_LAST /* last tag */ -}; - -/* ALC269 models */ -enum { - ALC269_BASIC, - ALC269_QUANTA_FL1, - ALC269_AMIC, - ALC269_DMIC, - ALC269VB_AMIC, - ALC269VB_DMIC, - ALC269_FUJITSU, - ALC269_LIFEBOOK, - ALC271_ACER, - ALC269_AUTO, - ALC269_MODEL_LAST /* last tag */ -}; - -/* ALC861 models */ -enum { - ALC861_3ST, - ALC660_3ST, - ALC861_3ST_DIG, - ALC861_6ST_DIG, - ALC861_UNIWILL_M31, - ALC861_TOSHIBA, - ALC861_ASUS, - ALC861_ASUS_LAPTOP, - ALC861_AUTO, - ALC861_MODEL_LAST, -}; - -/* ALC861-VD models */ -enum { - ALC660VD_3ST, - ALC660VD_3ST_DIG, - ALC660VD_ASUS_V1S, - ALC861VD_3ST, - ALC861VD_3ST_DIG, - ALC861VD_6ST_DIG, - ALC861VD_LENOVO, - ALC861VD_DALLAS, - ALC861VD_HP, - ALC861VD_AUTO, - ALC861VD_MODEL_LAST, -}; - -/* ALC662 models */ -enum { - ALC662_3ST_2ch_DIG, - ALC662_3ST_6ch_DIG, - ALC662_3ST_6ch, - ALC662_5ST_DIG, - ALC662_LENOVO_101E, - ALC662_ASUS_EEEPC_P701, - ALC662_ASUS_EEEPC_EP20, - ALC663_ASUS_M51VA, - ALC663_ASUS_G71V, - ALC663_ASUS_H13, - ALC663_ASUS_G50V, - ALC662_ECS, - ALC663_ASUS_MODE1, - ALC662_ASUS_MODE2, - ALC663_ASUS_MODE3, - ALC663_ASUS_MODE4, - ALC663_ASUS_MODE5, - ALC663_ASUS_MODE6, - ALC663_ASUS_MODE7, - ALC663_ASUS_MODE8, - ALC272_DELL, - ALC272_DELL_ZM1, - ALC272_SAMSUNG_NC10, - ALC662_AUTO, - ALC662_MODEL_LAST, -}; - -/* ALC882 models */ -enum { - ALC882_3ST_DIG, - ALC882_6ST_DIG, - ALC882_ARIMA, - ALC882_W2JC, - ALC882_TARGA, - ALC882_ASUS_A7J, - ALC882_ASUS_A7M, - ALC885_MACPRO, - ALC885_MBA21, - ALC885_MBP3, - ALC885_MB5, - ALC885_MACMINI3, - ALC885_IMAC24, - ALC885_IMAC91, - ALC883_3ST_2ch_DIG, - ALC883_3ST_6ch_DIG, - ALC883_3ST_6ch, - ALC883_6ST_DIG, - ALC883_TARGA_DIG, - ALC883_TARGA_2ch_DIG, - ALC883_TARGA_8ch_DIG, - ALC883_ACER, - ALC883_ACER_ASPIRE, - ALC888_ACER_ASPIRE_4930G, - ALC888_ACER_ASPIRE_6530G, - ALC888_ACER_ASPIRE_8930G, - ALC888_ACER_ASPIRE_7730G, - ALC883_MEDION, - ALC883_MEDION_WIM2160, - ALC883_LAPTOP_EAPD, - ALC883_LENOVO_101E_2ch, - ALC883_LENOVO_NB0763, - ALC888_LENOVO_MS7195_DIG, - ALC888_LENOVO_SKY, - ALC883_HAIER_W66, - ALC888_3ST_HP, - ALC888_6ST_DELL, - ALC883_MITAC, - ALC883_CLEVO_M540R, - ALC883_CLEVO_M720, - ALC883_FUJITSU_PI2515, - ALC888_FUJITSU_XA3530, - ALC883_3ST_6ch_INTEL, - ALC889A_INTEL, - ALC889_INTEL, - ALC888_ASUS_M90V, - ALC888_ASUS_EEE1601, - ALC889A_MB31, - ALC1200_ASUS_P5Q, - ALC883_SONY_VAIO_TT, - ALC882_AUTO, - ALC882_MODEL_LAST, -}; - -/* ALC680 models */ -enum { - ALC680_BASE, - ALC680_AUTO, - ALC680_MODEL_LAST, -}; +/* unsol event tags */ +#define ALC_FRONT_EVENT 0x01 +#define ALC_DCVOL_EVENT 0x02 +#define ALC_HP_EVENT 0x04 +#define ALC_MIC_EVENT 0x08 /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -429,39 +204,7 @@ struct alc_spec { struct alc_multi_io multi_io[4]; }; -/* - * configuration template - to be copied to the spec instance - */ -struct alc_config_preset { - const struct snd_kcontrol_new *mixers[5]; /* should be identical size - * with spec - */ - const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ - const struct hda_verb *init_verbs[5]; - unsigned int num_dacs; - const hda_nid_t *dac_nids; - hda_nid_t dig_out_nid; /* optional */ - hda_nid_t hp_nid; /* optional */ - const hda_nid_t *slave_dig_outs; - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - const hda_nid_t *capsrc_nids; - hda_nid_t dig_in_nid; - unsigned int num_channel_mode; - const struct hda_channel_mode *channel_mode; - int need_dac_fix; - int const_channel_count; - unsigned int num_mux_defs; - const struct hda_input_mux *input_mux; - void (*unsol_event)(struct hda_codec *, unsigned int); - void (*setup)(struct hda_codec *); - void (*init_hook)(struct hda_codec *); -#ifdef CONFIG_SND_HDA_POWER_SAVE - const struct hda_amp_list *loopbacks; - void (*power_hook)(struct hda_codec *codec); -#endif -}; - +#define ALC_MODEL_AUTO 0 /* common for all chips */ /* * input MUX handling @@ -567,345 +310,6 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[0], false); } -/* - * channel mode setting - */ -static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, - spec->num_channel_mode); -} - -static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - spec->ext_channel_count); -} - -static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->ext_channel_count); - if (err >= 0 && !spec->const_channel_count) { - spec->multiout.max_channels = spec->ext_channel_count; - if (spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - } - return err; -} - -/* - * Control the mode of pin widget settings via the mixer. "pc" is used - * instead of "%" to avoid consequences of accidentally treating the % as - * being part of a format specifier. Maximum allowed length of a value is - * 63 characters plus NULL terminator. - * - * Note: some retasking pin complexes seem to ignore requests for input - * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these - * are requested. Therefore order this list so that this behaviour will not - * cause problems when mixer clients move through the enum sequentially. - * NIDs 0x0f and 0x10 have been observed to have this behaviour as of - * March 2006. - */ -static const char * const alc_pin_mode_names[] = { - "Mic 50pc bias", "Mic 80pc bias", - "Line in", "Line out", "Headphone out", -}; -static const unsigned char alc_pin_mode_values[] = { - PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP, -}; -/* The control can present all 5 options, or it can limit the options based - * in the pin being assumed to be exclusively an input or an output pin. In - * addition, "input" pins may or may not process the mic bias option - * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to - * accept requests for bias as of chip versions up to March 2006) and/or - * wiring in the computer. - */ -#define ALC_PIN_DIR_IN 0x00 -#define ALC_PIN_DIR_OUT 0x01 -#define ALC_PIN_DIR_INOUT 0x02 -#define ALC_PIN_DIR_IN_NOMICBIAS 0x03 -#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04 - -/* Info about the pin modes supported by the different pin direction modes. - * For each direction the minimum and maximum values are given. - */ -static const signed char alc_pin_mode_dir_info[5][2] = { - { 0, 2 }, /* ALC_PIN_DIR_IN */ - { 3, 4 }, /* ALC_PIN_DIR_OUT */ - { 0, 4 }, /* ALC_PIN_DIR_INOUT */ - { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */ - { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */ -}; -#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0]) -#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1]) -#define alc_pin_mode_n_items(_dir) \ - (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1) - -static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int item_num = uinfo->value.enumerated.item; - unsigned char dir = (kcontrol->private_value >> 16) & 0xff; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = alc_pin_mode_n_items(dir); - - if (item_numalc_pin_mode_max(dir)) - item_num = alc_pin_mode_min(dir); - strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]); - return 0; -} - -static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int i; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char dir = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0x00); - - /* Find enumerated value for current pinctl setting */ - i = alc_pin_mode_min(dir); - while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl) - i++; - *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); - return 0; -} - -static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - signed int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char dir = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0x00); - - if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) - val = alc_pin_mode_min(dir); - - change = pinctl != alc_pin_mode_values[val]; - if (change) { - /* Set pin mode to that requested */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - alc_pin_mode_values[val]); - - /* Also enable the retasking pin's input/output as required - * for the requested pin mode. Enum values of 2 or less are - * input modes. - * - * Dynamically switching the input/output buffers probably - * reduces noise slightly (particularly on input) so we'll - * do it. However, having both input and output buffers - * enabled simultaneously doesn't seem to be problematic if - * this turns out to be necessary in the future. - */ - if (val <= 2) { - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, 0); - } else { - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - } - } - return change; -} - -#define ALC_PIN_MODE(xname, nid, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_pin_mode_info, \ - .get = alc_pin_mode_get, \ - .put = alc_pin_mode_put, \ - .private_value = nid | (dir<<16) } - -/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged - * together using a mask with more than one bit set. This control is - * currently used only by the ALC260 test model. At this stage they are not - * needed for any "production" models. - */ -#ifdef CONFIG_SND_DEBUG -#define alc_gpio_data_info snd_ctl_boolean_mono_info - -static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, 0x00); - - *valp = (val & mask) != 0; - return 0; -} -static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - signed int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, - 0x00); - - /* Set/unset the masked GPIO bit(s) as needed */ - change = (val == 0 ? 0 : mask) != (gpio_data & mask); - if (val == 0) - gpio_data &= ~mask; - else - gpio_data |= mask; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_GPIO_DATA, gpio_data); - - return change; -} -#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_gpio_data_info, \ - .get = alc_gpio_data_get, \ - .put = alc_gpio_data_put, \ - .private_value = nid | (mask<<16) } -#endif /* CONFIG_SND_DEBUG */ - -/* A switch control to allow the enabling of the digital IO pins on the - * ALC260. This is incredibly simplistic; the intention of this control is - * to provide something in the test model allowing digital outputs to be - * identified if present. If models are found which can utilise these - * outputs a more complete mixer control can be devised for those models if - * necessary. - */ -#ifdef CONFIG_SND_DEBUG -#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info - -static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0x00); - - *valp = (val & mask) != 0; - return 0; -} -static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - signed int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, - 0x00); - - /* Set/unset the masked control bit(s) as needed */ - change = (val == 0 ? 0 : mask) != (ctrl_data & mask); - if (val==0) - ctrl_data &= ~mask; - else - ctrl_data |= mask; - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - ctrl_data); - - return change; -} -#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_spdif_ctrl_info, \ - .get = alc_spdif_ctrl_get, \ - .put = alc_spdif_ctrl_put, \ - .private_value = nid | (mask<<16) } -#endif /* CONFIG_SND_DEBUG */ - -/* A switch control to allow the enabling EAPD digital outputs on the ALC26x. - * Again, this is only used in the ALC26x test models to help identify when - * the EAPD line must be asserted for features to work. - */ -#ifdef CONFIG_SND_DEBUG -#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info - -static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0x00); - - *valp = (val & mask) != 0; - return 0; -} - -static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, - 0x00); - - /* Set/unset the masked control bit(s) as needed */ - change = (!val ? 0 : mask) != (ctrl_data & mask); - if (!val) - ctrl_data &= ~mask; - else - ctrl_data |= mask; - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - ctrl_data); - - return change; -} - -#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_eapd_ctrl_info, \ - .get = alc_eapd_ctrl_get, \ - .put = alc_eapd_ctrl_put, \ - .private_value = nid | (mask<<16) } -#endif /* CONFIG_SND_DEBUG */ - /* * set up the input pin config (depending on the given auto-pin type) */ @@ -934,29 +338,10 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } -static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!cfg->line_outs) { - while (cfg->line_outs < AUTO_CFG_MAX_OUTS && - cfg->line_out_pins[cfg->line_outs]) - cfg->line_outs++; - } - if (!cfg->speaker_outs) { - while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS && - cfg->speaker_pins[cfg->speaker_outs]) - cfg->speaker_outs++; - } - if (!cfg->hp_outs) { - while (cfg->hp_outs < AUTO_CFG_MAX_OUTS && - cfg->hp_pins[cfg->hp_outs]) - cfg->hp_outs++; - } -} - /* + * Append the given mixer and verb elements for the later use + * The mixer array is referred in build_controls(), and init_verbs are + * called in init(). */ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix) { @@ -973,61 +358,8 @@ static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) } /* - * set up from the preset table + * GPIO setup tables, used in initialization */ -static void setup_preset(struct hda_codec *codec, - const struct alc_config_preset *preset) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) - add_mixer(spec, preset->mixers[i]); - spec->cap_mixer = preset->cap_mixer; - for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; - i++) - add_verb(spec, preset->init_verbs[i]); - - spec->channel_mode = preset->channel_mode; - spec->num_channel_mode = preset->num_channel_mode; - spec->need_dac_fix = preset->need_dac_fix; - spec->const_channel_count = preset->const_channel_count; - - if (preset->const_channel_count) - spec->multiout.max_channels = preset->const_channel_count; - else - spec->multiout.max_channels = spec->channel_mode[0].channels; - spec->ext_channel_count = spec->channel_mode[0].channels; - - spec->multiout.num_dacs = preset->num_dacs; - spec->multiout.dac_nids = preset->dac_nids; - spec->multiout.dig_out_nid = preset->dig_out_nid; - spec->multiout.slave_dig_outs = preset->slave_dig_outs; - spec->multiout.hp_nid = preset->hp_nid; - - spec->num_mux_defs = preset->num_mux_defs; - if (!spec->num_mux_defs) - spec->num_mux_defs = 1; - spec->input_mux = preset->input_mux; - - spec->num_adc_nids = preset->num_adc_nids; - spec->adc_nids = preset->adc_nids; - spec->capsrc_nids = preset->capsrc_nids; - spec->dig_in_nid = preset->dig_in_nid; - - spec->unsol_event = preset->unsol_event; - spec->init_hook = preset->init_hook; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->power_hook = preset->power_hook; - spec->loopback.amplist = preset->loopbacks; -#endif - - if (preset->setup) - preset->setup(codec); - - alc_fixup_autocfg_pin_nums(codec); -} - /* Enable GPIO mask and set output */ static const struct hda_verb alc_gpio1_init_verbs[] = { {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, @@ -1082,6 +414,11 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } +/* + * Jack-reporting via input-jack layer + */ + +/* initialization of jacks; currently checks only a few known pins */ static int alc_init_jacks(struct hda_codec *codec) { #ifdef CONFIG_SND_HDA_INPUT_JACK @@ -1117,7 +454,12 @@ static int alc_init_jacks(struct hda_codec *codec) return 0; } -static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +/* + * Jack detections for HP auto-mute and mic-switch + */ + +/* check each pin in the given array; returns true if any of them is plugged */ +static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) { int i, present = 0; @@ -1131,6 +473,7 @@ static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) return present; } +/* standard HP/line-out auto-mute helper */ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, bool mute, bool hp_out) { @@ -1201,6 +544,7 @@ static void update_speakers(struct hda_codec *codec) spec->autocfg.line_out_pins, on, false); } +/* standard HP-automute helper */ static void alc_hp_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1213,6 +557,7 @@ static void alc_hp_automute(struct hda_codec *codec) update_speakers(codec); } +/* standard line-out-automute helper */ static void alc_line_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1228,6 +573,7 @@ static void alc_line_automute(struct hda_codec *codec) #define get_connection_index(codec, mux, nid) \ snd_hda_get_conn_index(codec, mux, nid, 0) +/* standard mic auto-switch helper */ static void alc_mic_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1261,18 +607,19 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) else res >>= 26; switch (res) { - case ALC880_HP_EVENT: + case ALC_HP_EVENT: alc_hp_automute(codec); break; - case ALC880_FRONT_EVENT: + case ALC_FRONT_EVENT: alc_line_automute(codec); break; - case ALC880_MIC_EVENT: + case ALC_MIC_EVENT: alc_mic_automute(codec); break; } } +/* call init functions of standard auto-mute helpers */ static void alc_inithook(struct hda_codec *codec) { alc_hp_automute(codec); @@ -1298,6 +645,7 @@ static void alc888_coef_init(struct hda_codec *codec) AC_VERB_SET_PROC_COEF, 0x3030); } +/* additional initialization for ALC889 variants */ static void alc889_coef_init(struct hda_codec *codec) { unsigned int tmp; @@ -1339,6 +687,7 @@ static void alc_eapd_shutup(struct hda_codec *codec) msleep(200); } +/* generic EAPD initialization */ static void alc_auto_init_amp(struct hda_codec *codec, int type) { unsigned int tmp; @@ -1398,6 +747,9 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) } } +/* + * Auto-Mute mode mixer enum support + */ static int alc_automute_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1484,7 +836,11 @@ static const struct snd_kcontrol_new alc_automute_mode_enum = { .put = alc_automute_mode_put, }; -static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec); +static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec) +{ + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + return snd_array_new(&spec->kctls); +} static int alc_add_automute_mode_enum(struct hda_codec *codec) { @@ -1501,6 +857,10 @@ static int alc_add_automute_mode_enum(struct hda_codec *codec) return 0; } +/* + * Check the availability of HP/line-out auto-mute; + * Set up appropriately if really supported + */ static void alc_init_auto_hp(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1539,7 +899,7 @@ static void alc_init_auto_hp(struct hda_codec *codec) nid); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_HP_EVENT); + AC_USRSP_EN | ALC_HP_EVENT); spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; } @@ -1554,7 +914,7 @@ static void alc_init_auto_hp(struct hda_codec *codec) "on NID 0x%x\n", nid); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_FRONT_EVENT); + AC_USRSP_EN | ALC_FRONT_EVENT); spec->detect_line = 1; } spec->automute_lines = spec->detect_line; @@ -1567,6 +927,7 @@ static void alc_init_auto_hp(struct hda_codec *codec) } } +/* return the position of NID in the list, or -1 if not found */ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { int i; @@ -1576,7 +937,47 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) return -1; } -static bool alc_check_dyn_adc_switch(struct hda_codec *codec); +/* check whether dynamic ADC-switching is available */ +static bool alc_check_dyn_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, n, idx; + hda_nid_t cap, pin; + + if (imux != spec->input_mux) /* no dynamic imux? */ + return false; + + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!pin) + return false; + if (get_connection_index(codec, cap, pin) < 0) + break; + } + if (i >= imux->num_items) + return false; /* no ADC-switch is needed */ + } + + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + idx = get_connection_index(codec, cap, pin); + if (idx >= 0) { + imux->items[i].index = idx; + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("realtek: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + return true; +} /* rebuild imux for matching with the given auto-mic pins (if not yet) */ static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec) @@ -1637,17 +1038,21 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) snd_hda_codec_write_cache(codec, spec->ext_mic_pin, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_MIC_EVENT); + AC_USRSP_EN | ALC_MIC_EVENT); if (spec->dock_mic_pin) snd_hda_codec_write_cache(codec, spec->dock_mic_pin, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_MIC_EVENT); + AC_USRSP_EN | ALC_MIC_EVENT); spec->auto_mic_valid_imux = 1; spec->auto_mic = 1; return true; } +/* + * Check the availability of auto-mic switch; + * Set up if really supported + */ static void alc_init_auto_mic(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1713,6 +1118,17 @@ static void alc_init_auto_mic(struct hda_codec *codec) spec->unsol_event = alc_sku_unsol_event; } +/* check the availabilities of auto-mute and auto-mic switches */ +static void alc_auto_check_switches(struct hda_codec *codec) +{ + alc_init_auto_hp(codec); + alc_init_auto_mic(codec); +} + +/* + * Realtek SSID verification + */ + /* Could be any non-zero and even value. When used as fixup, tells * the driver to ignore any present sku defines. */ @@ -1783,6 +1199,7 @@ do_sku: return 0; } +/* return true if the given NID is found in the list */ static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { return find_idx_in_nid_list(nid, list, nums) >= 0; @@ -1917,13 +1334,6 @@ static void alc_ssid_check(struct hda_codec *codec, } } -/* check the availabilities of auto-mute and auto-mic switches */ -static void alc_auto_check_switches(struct hda_codec *codec) -{ - alc_init_auto_hp(codec); - alc_init_auto_mic(codec); -} - /* * Fix-up pin default configurations and add default verbs */ @@ -2069,6 +1479,9 @@ static void alc_pick_fixup(struct hda_codec *codec, } } +/* + * COEF access helper functions + */ static int alc_read_coef_idx(struct hda_codec *codec, unsigned int coef_idx) { @@ -2089,6 +1502,10 @@ static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx, coef_val); } +/* + * Digital I/O handling + */ + /* set right pin controls for digital I/O */ static void alc_auto_init_digital(struct hda_codec *codec) { @@ -2167,562 +1584,8 @@ static void alc_auto_parse_digital(struct hda_codec *codec) } /* - * ALC888 + * capture mixer elements */ - -/* - * 2ch mode - */ -static const struct hda_verb alc888_4ST_ch2_intel_init[] = { -/* Mic-in jack as mic in */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, -/* Line-in jack as Line in */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, -/* Line-Out as Front */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc888_4ST_ch4_intel_init[] = { -/* Mic-in jack as mic in */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, -/* Line-in jack as Surround */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-Out as Front */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc888_4ST_ch6_intel_init[] = { -/* Mic-in jack as CLFE */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-in jack as Surround */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc888_4ST_ch8_intel_init[] = { -/* Mic-in jack as CLFE */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-in jack as Surround */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-Out as Side */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - { } /* end */ -}; - -static const struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { - { 2, alc888_4ST_ch2_intel_init }, - { 4, alc888_4ST_ch4_intel_init }, - { 6, alc888_4ST_ch6_intel_init }, - { 8, alc888_4ST_ch8_intel_init }, -}; - -/* - * ALC888 Fujitsu Siemens Amillo xa3530 - */ - -static const struct hda_verb alc888_fujitsu_xa3530_verbs[] = { -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Connect Internal HP to Front */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Connect Bass HP to Front */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Connect Line-Out side jack (SPDIF) to Side */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, -/* Connect Mic jack to CLFE */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, -/* Connect Line-in jack to Surround */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, -/* Connect HP out jack to Front */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Enable unsolicited event for HP jack and Line-out jack */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {} -}; - -static void alc889_automute_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->autocfg.speaker_pins[3] = 0x19; - spec->autocfg.speaker_pins[4] = 0x1a; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc889_intel_init_hook(struct hda_codec *codec) -{ - alc889_coef_init(codec); - alc_hp_automute(codec); -} - -static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x17; /* line-out */ - spec->autocfg.hp_pins[1] = 0x1b; /* hp */ - spec->autocfg.speaker_pins[0] = 0x14; /* speaker */ - spec->autocfg.speaker_pins[1] = 0x15; /* bass */ - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* - * ALC888 Acer Aspire 4930G model - */ - -static const struct hda_verb alc888_acer_aspire_4930g_verbs[] = { -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Connect Internal HP to front */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Connect HP out to front */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - * ALC888 Acer Aspire 6530G model - */ - -static const struct hda_verb alc888_acer_aspire_6530g_verbs[] = { -/* Route to built-in subwoofer as well as speakers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -/* Bias voltage on for external mic port */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Enable speaker output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, -/* Enable headphone output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - *ALC888 Acer Aspire 7730G model - */ - -static const struct hda_verb alc888_acer_aspire_7730G_verbs[] = { -/* Bias voltage on for external mic port */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Enable speaker output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, -/* Enable headphone output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, -/*Enable internal subwoofer */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x17, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - * ALC889 Acer Aspire 8930G model - */ - -static const struct hda_verb alc889_acer_aspire_8930g_verbs[] = { -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Connect Internal Front to Front */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Connect Internal Rear to Rear */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, -/* Connect Internal CLFE to CLFE */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, -/* Connect HP out to Front */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Enable all DACs */ -/* DAC DISABLE/MUTE 1? */ -/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x03}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, -/* DAC DISABLE/MUTE 2? */ -/* some bit here disables the other DACs. Init=0x4900 */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, -/* DMIC fix - * This laptop has a stereo digital microphone. The mics are only 1cm apart - * which makes the stereo useless. However, either the mic or the ALC889 - * makes the signal become a difference/sum signal instead of standard - * stereo, which is annoying. So instead we flip this bit which makes the - * codec replicate the sum signal to both channels, turning it into a - * normal mono mic. - */ -/* DMIC_CONTROL? Init value = 0x0001 */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0003}, - { } -}; - -static const struct hda_input_mux alc888_2_capture_sources[2] = { - /* Front mic only available on one ADC */ - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Front Mic", 0xb }, - }, - }, - { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, - } -}; - -static const struct hda_input_mux alc888_acer_aspire_6530_sources[2] = { - /* Interal mic only available on one ADC */ - { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Line In", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - { "Internal Mic", 0xb }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line In", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - }, - } -}; - -static const struct hda_input_mux alc889_capture_sources[3] = { - /* Digital mic only available on first "ADC" */ - { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Front Mic", 0xb }, - { "Input Mix", 0xa }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - }, - } -}; - -static const struct snd_kcontrol_new alc888_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - - -static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* - * ALC880 3-stack model - * - * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) - * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, - * F-Mic = 0x1b, HP = 0x19 - */ - -static const hda_nid_t alc880_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x05, 0x04, 0x03 -}; - -static const hda_nid_t alc880_adc_nids[3] = { - /* ADC0-2 */ - 0x07, 0x08, 0x09, -}; - -/* The datasheet says the node 0x07 is connected from inputs, - * but it shows zero connection in the real implementation on some devices. - * Note: this is a 915GAV bug, fixed on 915GLV - */ -static const hda_nid_t alc880_adc_nids_alt[2] = { - /* ADC1-2 */ - 0x08, 0x09, -}; - -#define ALC880_DIGOUT_NID 0x06 -#define ALC880_DIGIN_NID 0x0a - -static const struct hda_input_mux alc880_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x3 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* channel source setting (2/6 channel selection for 3-stack) */ -/* 2ch mode */ -static const struct hda_verb alc880_threestack_ch2_init[] = { - /* set line-in to input, mute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - /* set mic-in to input vref 80%, mute it */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* 6ch mode */ -static const struct hda_verb alc880_threestack_ch6_init[] = { - /* set line-in to output, unmute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - /* set mic-in to output, unmute it */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode alc880_threestack_modes[2] = { - { 2, alc880_threestack_ch2_init }, - { 6, alc880_threestack_ch6_init }, -}; - -static const struct snd_kcontrol_new alc880_three_stack_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -/* capture mixer elements */ static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2886,400 +1749,71 @@ DEFINE_CAPMIX_NOSRC(2); DEFINE_CAPMIX_NOSRC(3); /* - * ALC880 5-stack model - * - * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), - * Side = 0x02 (0xd) - * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 - * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 + * virtual master controls */ -/* additional mixers to alc880_three_stack_mixer */ -static const struct snd_kcontrol_new alc880_five_stack_mixer[] = { - HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT), - { } /* end */ -}; - -/* channel source setting (6/8 channel selection for 5-stack) */ -/* 6ch mode */ -static const struct hda_verb alc880_fivestack_ch6_init[] = { - /* set line-in to input, mute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* 8ch mode */ -static const struct hda_verb alc880_fivestack_ch8_init[] = { - /* set line-in to output, unmute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode alc880_fivestack_modes[2] = { - { 6, alc880_fivestack_ch6_init }, - { 8, alc880_fivestack_ch8_init }, -}; - - /* - * ALC880 6-stack model - * - * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), - * Side = 0x05 (0x0f) - * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, - * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b + * slave controls for virtual master */ - -static const hda_nid_t alc880_6st_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x03, 0x04, 0x05 -}; - -static const struct hda_input_mux alc880_6stack_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* fixed 8-channels */ -static const struct hda_channel_mode alc880_sixstack_modes[1] = { - { 8, NULL }, +static const char * const alc_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + "Mono Playback Volume", + "Line-Out Playback Volume", + NULL, }; -static const struct snd_kcontrol_new alc880_six_stack_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ +static const char * const alc_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Speaker Playback Switch", + "Mono Playback Switch", + "IEC958 Playback Switch", + "Line-Out Playback Switch", + NULL, }; - /* - * ALC880 W810 model - * - * W810 has rear IO for: - * Front (DAC 02) - * Surround (DAC 03) - * Center/LFE (DAC 04) - * Digital out (06) - * - * The system also has a pair of internal speakers, and a headphone jack. - * These are both connected to Line2 on the codec, hence to DAC 02. - * - * There is a variable resistor to control the speaker or headphone - * volume. This is a hardware-only device without a software API. - * - * Plugging headphones in will disable the internal speakers. This is - * implemented in hardware, not via the driver using jack sense. In - * a similar fashion, plugging into the rear socket marked "front" will - * disable both the speakers and headphones. - * - * For input, there's a microphone jack, and an "audio in" jack. - * These may not do anything useful with this driver yet, because I - * haven't setup any initialization verbs for these yet... + * build control elements */ -static const hda_nid_t alc880_w810_dac_nids[3] = { - /* front, rear/surround, clfe */ - 0x02, 0x03, 0x04 -}; - -/* fixed 6 channels */ -static const struct hda_channel_mode alc880_w810_modes[1] = { - { 6, NULL } -}; - -/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */ -static const struct snd_kcontrol_new alc880_w810_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - { } /* end */ -}; - - -/* - * Z710V model - * - * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) - * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), - * Line = 0x1a - */ +#define NID_MAPPING (-1) -static const hda_nid_t alc880_z71v_dac_nids[1] = { - 0x02 -}; -#define ALC880_Z71V_HP_DAC 0x03 +#define SUBDEV_SPEAKER_ (0 << 6) +#define SUBDEV_HP_ (1 << 6) +#define SUBDEV_LINE_ (2 << 6) +#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) +#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) +#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) -/* fixed 2 channels */ -static const struct hda_channel_mode alc880_2_jack_modes[1] = { - { 2, NULL } -}; +static void alc_free_kctls(struct hda_codec *codec); -static const struct snd_kcontrol_new alc880_z71v_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new alc_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; +#endif - -/* - * ALC880 F1734 model - * - * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d) - * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18 - */ - -static const hda_nid_t alc880_f1734_dac_nids[1] = { - 0x03 -}; -#define ALC880_F1734_HP_DAC 0x02 - -static const struct snd_kcontrol_new alc880_f1734_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_input_mux alc880_f1734_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "CD", 0x4 }, - }, -}; - - -/* - * ALC880 ASUS model - * - * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) - * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, - * Mic = 0x18, Line = 0x1a - */ - -#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */ -#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */ - -static const struct snd_kcontrol_new alc880_asus_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -/* - * ALC880 ASUS W1V model - * - * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) - * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, - * Mic = 0x18, Line = 0x1a, Line2 = 0x1b - */ - -/* additional mixers to alc880_asus_mixer */ -static const struct snd_kcontrol_new alc880_asus_w1v_mixer[] = { - HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT), - { } /* end */ -}; - -/* TCL S700 */ -static const struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, 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("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* Uniwill */ -static const struct snd_kcontrol_new alc880_uniwill_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc880_fujitsu_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* - * virtual master controls - */ - -/* - * slave controls for virtual master - */ -static const char * const alc_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Speaker Playback Volume", - "Mono Playback Volume", - "Line-Out Playback Volume", - NULL, -}; - -static const char * const alc_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Speaker Playback Switch", - "Mono Playback Switch", - "IEC958 Playback Switch", - "Line-Out Playback Switch", - NULL, -}; - -/* - * build control elements - */ - -#define NID_MAPPING (-1) - -#define SUBDEV_SPEAKER_ (0 << 6) -#define SUBDEV_HP_ (1 << 6) -#define SUBDEV_LINE_ (2 << 6) -#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) -#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) -#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) - -static void alc_free_kctls(struct hda_codec *codec); - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), - { } /* end */ -}; -#endif - -static int alc_build_controls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct snd_kcontrol *kctl = NULL; - const struct snd_kcontrol_new *knew; - int i, j, err; - unsigned int u; - hda_nid_t nid; +static int alc_build_controls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct snd_kcontrol *kctl = NULL; + const struct snd_kcontrol_new *knew; + int i, j, err; + unsigned int u; + hda_nid_t nid; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -3425,910 +1959,127 @@ static int alc_build_controls(struct hda_codec *codec) /* - * initialize the codec volumes, etc - */ - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc880_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, 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)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - - /* - * Set up output mixers (0x0c - 0x0f) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, 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_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - { } -}; - -/* - * 3-stack pin configuration: - * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b - */ -static const struct hda_verb alc880_pin_3stack_init_verbs[] = { - /* - * preset connection lists of input pins - * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround - */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ - - /* - * Set pin mode and muting - */ - /* set front pin widgets 0x14 for output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mic2 (as headphone out) for HP output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line2 (as front mic) pin widget for input and vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* - * 5-stack pin configuration: - * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19, - * line-in/side = 0x1a, f-mic = 0x1b - */ -static const struct hda_verb alc880_pin_5stack_init_verbs[] = { - /* - * preset connection lists of input pins - * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround - */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */ - - /* - * Set pin mode and muting - */ - /* set pin widgets 0x14-0x17 for output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* unmute pins for output (no gain on this amp) */ - {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_UNMUTE}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mic2 (as headphone out) for HP output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line2 (as front mic) pin widget for input and vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* - * W810 pin configuration: - * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b + * Common callbacks */ -static const struct hda_verb alc880_pin_w810_init_verbs[] = { - /* hphone/speaker input selector: front DAC */ - {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +static void alc_init_special_input_src(struct hda_codec *codec); - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +static int alc_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int i; - { } -}; + alc_fix_pll(codec); + alc_auto_init_amp(codec, spec->init_amp); -/* - * Z71V pin configuration: - * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?) - */ -static const struct hda_verb alc880_pin_z71v_init_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + for (i = 0; i < spec->num_init_verbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); + alc_init_special_input_src(codec); - {0x18, 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_VREF80}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + if (spec->init_hook) + spec->init_hook(codec); - { } -}; + alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); -/* - * 6-stack pin configuration: - * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, - * f-mic = 0x19, line = 0x1a, HP = 0x1b - */ -static const struct hda_verb alc880_pin_6stack_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + hda_call_check_power_status(codec, 0x01); + return 0; +} - { } -}; +static void alc_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct alc_spec *spec = codec->spec; -/* - * Uniwill pin configuration: - * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19, - * line = 0x1a - */ -static const struct hda_verb alc880_uniwill_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */ - /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + if (spec->unsol_event) + spec->unsol_event(codec, res); +} - { } -}; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); +} +#endif /* -* Uniwill P53 -* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, + * Analog playback callbacks */ -static const struct hda_verb alc880_uniwill_p53_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT}, - - { } -}; - -static const struct hda_verb alc880_beep_init_verbs[] = { - { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) }, - { } -}; - -/* auto-toggle front mic */ -static void alc88x_simple_mic_automute(struct hda_codec *codec) +static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - unsigned int present; - unsigned char bits; - - present = snd_hda_jack_detect(codec, 0x18); - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } -static void alc880_uniwill_setup(struct hda_codec *codec) +static int alc_playback_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->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); } -static void alc880_uniwill_init_hook(struct hda_codec *codec) +static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - alc_hp_automute(codec); - alc88x_simple_mic_automute(codec); + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } -static void alc880_uniwill_unsol_event(struct hda_codec *codec, - unsigned int res) +/* + * Digital out + */ +static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - switch (res >> 28) { - case ALC880_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); } -static void alc880_uniwill_p53_setup(struct hda_codec *codec) +static int alc_dig_playback_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->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); } -static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) +static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - present &= HDA_AMP_VOLMASK; - snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, - HDA_AMP_VOLMASK, present); - snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0, - HDA_AMP_VOLMASK, present); + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); } -static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, - unsigned int res) +static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - if ((res >> 28) == ALC880_DCVOL_EVENT) - alc880_uniwill_p53_dcvol_automute(codec); - else - alc_sku_unsol_event(codec, res); + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); } /* - * F1734 pin configuration: - * HP = 0x14, speaker-out = 0x15, mic = 0x18 + * Analog capture */ -static const struct hda_verb alc880_pin_f1734_init_verbs[] = { - {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT}, - - { } -}; - -/* - * ASUS pin configuration: - * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a - */ -static const struct hda_verb alc880_pin_asus_init_verbs[] = { - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* Enable GPIO mask and set output */ -#define alc880_gpio1_init_verbs alc_gpio1_init_verbs -#define alc880_gpio2_init_verbs alc_gpio2_init_verbs -#define alc880_gpio3_init_verbs alc_gpio3_init_verbs - -/* Clevo m520g init */ -static const struct hda_verb alc880_pin_clevo_init_verbs[] = { - /* headphone output */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* line-out */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Line-in */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* CD */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Mic1 (rear panel) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Mic2 (front panel) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* headphone */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - - { } -}; - -static const struct hda_verb alc880_pin_tcl_S700_init_verbs[] = { - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - - /* Headphone output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Front output*/ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, - - { } -}; - -/* - * LG m1 express dual - * - * Pin assignment: - * Rear Line-In/Out (blue): 0x14 - * Build-in Mic-In: 0x15 - * Speaker-out: 0x17 - * HP-Out (green): 0x1b - * Mic-In/Out (red): 0x19 - * SPDIF-Out: 0x1e - */ - -/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */ -static const hda_nid_t alc880_lg_dac_nids[3] = { - 0x05, 0x02, 0x03 -}; - -/* seems analog CD is not working */ -static const struct hda_input_mux alc880_lg_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x1 }, - { "Line", 0x5 }, - { "Internal Mic", 0x6 }, - }, -}; - -/* 2,4,6 channel modes */ -static const struct hda_verb alc880_lg_ch2_init[] = { - /* set line-in and mic-in to input */ - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } -}; - -static const struct hda_verb alc880_lg_ch4_init[] = { - /* set line-in to out and mic-in to input */ - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } -}; - -static const struct hda_verb alc880_lg_ch6_init[] = { - /* set line-in and mic-in to output */ - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { } -}; - -static const struct hda_channel_mode alc880_lg_ch_modes[3] = { - { 2, alc880_lg_ch2_init }, - { 4, alc880_lg_ch4_init }, - { 6, alc880_lg_ch6_init }, -}; - -static const struct snd_kcontrol_new alc880_lg_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc880_lg_init_verbs[] = { - /* set capture source to mic-in */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* mute all amp mixer inputs */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* line-in to input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* built-in mic */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* speaker-out */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* mic-in to input */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* HP-out */ - {0x13, AC_VERB_SET_CONNECT_SEL, 0x03}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* jack sense */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* - * LG LW20 - * - * Pin assignment: - * Speaker-out: 0x14 - * Mic-In: 0x18 - * Built-in Mic-In: 0x19 - * Line-In: 0x1b - * HP-Out: 0x1a - * SPDIF-Out: 0x1e - */ - -static const struct hda_input_mux alc880_lg_lw_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Line In", 0x2 }, - }, -}; - -#define alc880_lg_lw_modes alc880_threestack_modes - -static const struct snd_kcontrol_new alc880_lg_lw_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - 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("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc880_lg_lw_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ - - /* set capture source to mic-in */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* speaker-out */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* HP-out */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* mic-in to input */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* built-in mic */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* jack sense */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_lw_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct snd_kcontrol_new alc880_medion_rim_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_input_mux alc880_medion_rim_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; - -static const struct hda_verb alc880_medion_rim_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mic2 (as headphone out) for HP output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Internal Speaker */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc880_medion_rim_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_hp_automute(codec); - /* toggle EAPD */ - if (spec->jack_present) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); - else - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2); -} - -static void alc880_medion_rim_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - if ((res >> 28) == ALC880_HP_EVENT) - alc880_medion_rim_automute(codec); -} - -static void alc880_medion_rim_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc880_loopbacks[] = { - { 0x0b, HDA_INPUT, 0 }, - { 0x0b, HDA_INPUT, 1 }, - { 0x0b, HDA_INPUT, 2 }, - { 0x0b, HDA_INPUT, 3 }, - { 0x0b, HDA_INPUT, 4 }, - { } /* end */ -}; - -static const struct hda_amp_list alc880_lg_loopbacks[] = { - { 0x0b, HDA_INPUT, 1 }, - { 0x0b, HDA_INPUT, 6 }, - { 0x0b, HDA_INPUT, 7 }, - { } /* end */ -}; -#endif - -/* - * Common callbacks - */ - -static void alc_init_special_input_src(struct hda_codec *codec); - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int i; - - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - alc_init_special_input_src(codec); - - if (spec->init_hook) - spec->init_hook(codec); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - - hda_call_check_power_status(codec, 0x01); - return 0; -} - -static void alc_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct alc_spec *spec = codec->spec; - - if (spec->unsol_event) - spec->unsol_event(codec, res); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - * Analog playback callbacks - */ -static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int alc_playback_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; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int alc_dig_playback_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; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int alc_alt_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; +static int alc_alt_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; snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], stream_tag, 0, format); @@ -4662,709 +2413,30 @@ static int alc_codec_rename(struct hda_codec *codec, const char *name) } /* - * Test configuration for debugging - * - * Almost all inputs/outputs are enabled. I/O pins can be configured via - * enum controls. + * Automatic parse of I/O pins from the BIOS configuration */ -#ifdef CONFIG_SND_DEBUG -static const hda_nid_t alc880_test_dac_nids[4] = { - 0x02, 0x03, 0x04, 0x05 -}; -static const struct hda_input_mux alc880_test_capture_source = { - .num_items = 7, - .items = { - { "In-1", 0x0 }, - { "In-2", 0x1 }, - { "In-3", 0x2 }, - { "In-4", 0x3 }, - { "CD", 0x4 }, - { "Front", 0x5 }, - { "Surround", 0x6 }, - }, +enum { + ALC_CTL_WIDGET_VOL, + ALC_CTL_WIDGET_MUTE, + ALC_CTL_BIND_MUTE, }; - -static const struct hda_channel_mode alc880_test_modes[4] = { - { 2, NULL }, - { 4, NULL }, - { 6, NULL }, - { 8, NULL }, +static const struct snd_kcontrol_new alc_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_BIND_MUTE(NULL, 0, 0, 0), }; -static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "N/A", "Line Out", "HP Out", - "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item >= 8) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - unsigned int pin_ctl, item = 0; - - pin_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (pin_ctl & AC_PINCTL_OUT_EN) { - if (pin_ctl & AC_PINCTL_HP_EN) - item = 2; - else - item = 1; - } else if (pin_ctl & AC_PINCTL_IN_EN) { - switch (pin_ctl & AC_PINCTL_VREFEN) { - case AC_PINCTL_VREF_HIZ: item = 3; break; - case AC_PINCTL_VREF_50: item = 4; break; - case AC_PINCTL_VREF_GRD: item = 5; break; - case AC_PINCTL_VREF_80: item = 6; break; - case AC_PINCTL_VREF_100: item = 7; break; - } - } - ucontrol->value.enumerated.item[0] = item; - return 0; -} - -static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - static const unsigned int ctls[] = { - 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_50, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_80, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_100, - }; - unsigned int old_ctl, new_ctl; - - old_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - new_ctl = ctls[ucontrol->value.enumerated.item[0]]; - if (old_ctl != new_ctl) { - int val; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - new_ctl); - val = ucontrol->value.enumerated.item[0] >= 3 ? - HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, val); - return 1; - } - return 0; -} - -static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "Front", "Surround", "CLFE", "Side" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - unsigned int sel; - - sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); - ucontrol->value.enumerated.item[0] = sel & 3; - return 0; -} - -static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - unsigned int sel; - - sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3; - if (ucontrol->value.enumerated.item[0] != sel) { - sel = ucontrol->value.enumerated.item[0] & 3; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, sel); - return 1; - } - return 0; -} - -#define PIN_CTL_TEST(xname,nid) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_test_pin_ctl_info, \ - .get = alc_test_pin_ctl_get, \ - .put = alc_test_pin_ctl_put, \ - .private_value = nid \ - } - -#define PIN_SRC_TEST(xname,nid) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_test_pin_src_info, \ - .get = alc_test_pin_src_get, \ - .put = alc_test_pin_src_put, \ - .private_value = nid \ - } - -static const struct snd_kcontrol_new alc880_test_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - PIN_CTL_TEST("Front Pin Mode", 0x14), - PIN_CTL_TEST("Surround Pin Mode", 0x15), - PIN_CTL_TEST("CLFE Pin Mode", 0x16), - PIN_CTL_TEST("Side Pin Mode", 0x17), - PIN_CTL_TEST("In-1 Pin Mode", 0x18), - PIN_CTL_TEST("In-2 Pin Mode", 0x19), - PIN_CTL_TEST("In-3 Pin Mode", 0x1a), - PIN_CTL_TEST("In-4 Pin Mode", 0x1b), - PIN_SRC_TEST("In-1 Pin Source", 0x18), - PIN_SRC_TEST("In-2 Pin Source", 0x19), - PIN_SRC_TEST("In-3 Pin Source", 0x1a), - PIN_SRC_TEST("In-4 Pin Source", 0x1b), - HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc880_test_init_verbs[] = { - /* Unmute inputs of 0x0c - 0x0f */ - {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)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Vol output for 0x0c-0x0f */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Set output pins 0x14-0x17 */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Unmute output pins 0x14-0x17 */ - {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_UNMUTE}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Set input pins 0x18-0x1c */ - {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}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Mute input pins 0x18-0x1b */ - {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}, - /* ADC set up */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Analog input/passthru */ - {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)}, - { } -}; -#endif - -/* - */ - -static const char * const alc880_models[ALC880_MODEL_LAST] = { - [ALC880_3ST] = "3stack", - [ALC880_TCL_S700] = "tcl", - [ALC880_3ST_DIG] = "3stack-digout", - [ALC880_CLEVO] = "clevo", - [ALC880_5ST] = "5stack", - [ALC880_5ST_DIG] = "5stack-digout", - [ALC880_W810] = "w810", - [ALC880_Z71V] = "z71v", - [ALC880_6ST] = "6stack", - [ALC880_6ST_DIG] = "6stack-digout", - [ALC880_ASUS] = "asus", - [ALC880_ASUS_W1V] = "asus-w1v", - [ALC880_ASUS_DIG] = "asus-dig", - [ALC880_ASUS_DIG2] = "asus-dig2", - [ALC880_UNIWILL_DIG] = "uniwill", - [ALC880_UNIWILL_P53] = "uniwill-p53", - [ALC880_FUJITSU] = "fujitsu", - [ALC880_F1734] = "F1734", - [ALC880_LG] = "lg", - [ALC880_LG_LW] = "lg-lw", - [ALC880_MEDION_RIM] = "medion", -#ifdef CONFIG_SND_DEBUG - [ALC880_TEST] = "test", -#endif - [ALC880_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc880_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), - SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), - SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), - SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V), - /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ - SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), - SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */ - SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), - SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), - SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_F1734), - SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), - SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), - SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), - SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_F1734), - SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), - SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), - SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */ - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), - /* default Intel */ - SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), - {} -}; - -/* - * ALC880 codec presets - */ -static const struct alc_config_preset alc880_presets[] = { - [ALC880_3ST] = { - .mixers = { alc880_three_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_3ST_DIG] = { - .mixers = { alc880_three_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_TCL_S700] = { - .mixers = { alc880_tcl_s700_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_tcl_S700_init_verbs, - alc880_gpio2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */ - .num_adc_nids = 1, /* single ADC */ - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_5ST] = { - .mixers = { alc880_three_stack_mixer, - alc880_five_stack_mixer}, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_5stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), - .channel_mode = alc880_fivestack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_5ST_DIG] = { - .mixers = { alc880_three_stack_mixer, - alc880_five_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_5stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), - .channel_mode = alc880_fivestack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_6ST] = { - .mixers = { alc880_six_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_6stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), - .dac_nids = alc880_6st_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), - .channel_mode = alc880_sixstack_modes, - .input_mux = &alc880_6stack_capture_source, - }, - [ALC880_6ST_DIG] = { - .mixers = { alc880_six_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_6stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), - .dac_nids = alc880_6st_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), - .channel_mode = alc880_sixstack_modes, - .input_mux = &alc880_6stack_capture_source, - }, - [ALC880_W810] = { - .mixers = { alc880_w810_base_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_w810_init_verbs, - alc880_gpio2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids), - .dac_nids = alc880_w810_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), - .channel_mode = alc880_w810_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_Z71V] = { - .mixers = { alc880_z71v_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_z71v_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids), - .dac_nids = alc880_z71v_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_F1734] = { - .mixers = { alc880_f1734_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_f1734_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids), - .dac_nids = alc880_f1734_dac_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_f1734_capture_source, - .unsol_event = alc880_uniwill_p53_unsol_event, - .setup = alc880_uniwill_p53_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_ASUS] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_ASUS_DIG] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_ASUS_DIG2] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio2_init_verbs }, /* use GPIO2 */ - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_ASUS_W1V] = { - .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_UNIWILL_DIG] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_UNIWILL] = { - .mixers = { alc880_uniwill_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_uniwill_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - .unsol_event = alc880_uniwill_unsol_event, - .setup = alc880_uniwill_setup, - .init_hook = alc880_uniwill_init_hook, - }, - [ALC880_UNIWILL_P53] = { - .mixers = { alc880_uniwill_p53_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_uniwill_p53_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), - .channel_mode = alc880_threestack_modes, - .input_mux = &alc880_capture_source, - .unsol_event = alc880_uniwill_p53_unsol_event, - .setup = alc880_uniwill_p53_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_FUJITSU] = { - .mixers = { alc880_fujitsu_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_uniwill_p53_init_verbs, - alc880_beep_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, - .unsol_event = alc880_uniwill_p53_unsol_event, - .setup = alc880_uniwill_p53_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_CLEVO] = { - .mixers = { alc880_three_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_clevo_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_LG] = { - .mixers = { alc880_lg_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_lg_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids), - .dac_nids = alc880_lg_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes), - .channel_mode = alc880_lg_ch_modes, - .need_dac_fix = 1, - .input_mux = &alc880_lg_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc880_lg_setup, - .init_hook = alc_hp_automute, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .loopbacks = alc880_lg_loopbacks, -#endif - }, - [ALC880_LG_LW] = { - .mixers = { alc880_lg_lw_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_lg_lw_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes), - .channel_mode = alc880_lg_lw_modes, - .input_mux = &alc880_lg_lw_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc880_lg_lw_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_MEDION_RIM] = { - .mixers = { alc880_medion_rim_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_medion_rim_init_verbs, - alc_gpio2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_medion_rim_capture_source, - .unsol_event = alc880_medion_rim_unsol_event, - .setup = alc880_medion_rim_setup, - .init_hook = alc880_medion_rim_automute, - }, -#ifdef CONFIG_SND_DEBUG - [ALC880_TEST] = { - .mixers = { alc880_test_mixer }, - .init_verbs = { alc880_test_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_test_dac_nids), - .dac_nids = alc880_test_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_test_modes), - .channel_mode = alc880_test_modes, - .input_mux = &alc880_test_capture_source, - }, -#endif -}; - -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -enum { - ALC_CTL_WIDGET_VOL, - ALC_CTL_WIDGET_MUTE, - ALC_CTL_BIND_MUTE, -}; -static const struct snd_kcontrol_new alc880_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), -}; - -static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec) -{ - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - return snd_array_new(&spec->kctls); -} - -/* add dynamic controls */ -static int add_control(struct alc_spec *spec, int type, const char *name, - int cidx, unsigned long val) +/* add dynamic controls */ +static int add_control(struct alc_spec *spec, int type, const char *name, + int cidx, unsigned long val) { struct snd_kcontrol_new *knew; knew = alc_kcontrol_new(spec); if (!knew) return -ENOMEM; - *knew = alc880_control_templates[type]; + *knew = alc_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; @@ -5393,16 +2465,6 @@ static int add_control_with_pfx(struct alc_spec *spec, int type, #define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) -#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) -#define alc880_fixed_pin_idx(nid) ((nid) - 0x14) -#define alc880_is_multi_pin(nid) ((nid) >= 0x18) -#define alc880_multi_pin_idx(nid) ((nid) - 0x18) -#define alc880_idx_to_dac(nid) ((nid) + 0x02) -#define alc880_dac_to_idx(nid) ((nid) - 0x02) -#define alc880_idx_to_mixer(nid) ((nid) + 0x0c) -#define alc880_idx_to_selector(nid) ((nid) + 0x10) -#define ALC880_PIN_CD_NID 0x1c - static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, bool can_be_master, int *index) { @@ -5459,6 +2521,7 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) return (pincap & AC_PINCAP_IN) != 0; } +/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ static int alc_auto_fill_adc_caps(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5568,14 +2631,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) return 0; } -static int alc_auto_fill_dac_nids(struct hda_codec *codec); -static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg); -static int alc_auto_create_hp_out(struct hda_codec *codec); -static int alc_auto_create_speaker_out(struct hda_codec *codec); -static void alc_auto_init_multi_out(struct hda_codec *codec); -static void alc_auto_init_extra_out(struct hda_codec *codec); - static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_type) { @@ -5621,8622 +2676,1103 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) } } -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, - int (*fill_dac)(struct hda_codec *)); -static void alc_remove_invalid_adc_nids(struct hda_codec *codec); -static void alc_auto_init_input_src(struct hda_codec *codec); - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -static int alc880_parse_auto_config(struct hda_codec *codec) +/* convert from MIX nid to DAC */ +static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) { - struct alc_spec *spec = codec->spec; - int err; - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc880_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); + hda_nid_t list[5]; + int i, num; - return 1; + num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); + for (i = 0; i < num; i++) { + if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) + return list[i]; + } + return 0; } -/* additional initialization for auto-configuration model */ -static void alc880_auto_init(struct hda_codec *codec) +/* go down to the selector widget before the mixer */ +static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) { - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + hda_nid_t srcs[5]; + int num = snd_hda_get_connections(codec, pin, srcs, + ARRAY_SIZE(srcs)); + if (num != 1 || + get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) + return pin; + return srcs[0]; } -/* select or unmute the given capsrc route */ -static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, - int idx) +/* get MIX nid connected to the given pin targeted to DAC */ +static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) { - if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { - snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, - HDA_AMP_MUTE, 0); - } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) { - snd_hda_codec_write_cache(codec, cap, 0, - AC_VERB_SET_CONNECT_SEL, idx); + hda_nid_t mix[5]; + int i, num; + + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, mix[i]) == dac) + return mix[i]; } + return 0; } -/* set the default connection to that pin */ -static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) +/* select the connection from pin to DAC if needed */ +static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) { - struct alc_spec *spec = codec->spec; - int i; + hda_nid_t mix[5]; + int i, num; - if (!pin) + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + if (num < 2) return 0; - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = spec->capsrc_nids ? - spec->capsrc_nids[i] : spec->adc_nids[i]; - int idx; - - idx = get_connection_index(codec, cap, pin); - if (idx < 0) - continue; - select_or_unmute_capsrc(codec, cap, idx); - return i; /* return the found index */ + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, i); + return 0; + } } - return -1; /* not found */ + return 0; } -/* initialize some special cases for input sources */ -static void alc_init_special_input_src(struct hda_codec *codec) +/* look for an empty DAC slot */ +static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; - int i; + hda_nid_t srcs[5]; + int i, num; - for (i = 0; i < spec->autocfg.num_inputs; i++) - init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); + for (i = 0; i < num; i++) { + hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); + if (!nid) + continue; + if (found_in_nid_list(nid, spec->multiout.dac_nids, + spec->multiout.num_dacs)) + continue; + if (spec->multiout.hp_nid == nid) + continue; + if (found_in_nid_list(nid, spec->multiout.extra_out_nid, + ARRAY_SIZE(spec->multiout.extra_out_nid))) + continue; + return nid; + } + return 0; } -static void set_capture_mixer(struct hda_codec *codec) +static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { - struct alc_spec *spec = codec->spec; - static const struct snd_kcontrol_new *caps[2][3] = { - { alc_capture_mixer_nosrc1, - alc_capture_mixer_nosrc2, - alc_capture_mixer_nosrc3 }, - { alc_capture_mixer1, - alc_capture_mixer2, - alc_capture_mixer3 }, - }; - - /* check whether either of ADC or MUX has a volume control */ - if (!(query_amp_caps(codec, spec->adc_nids[0], HDA_INPUT) & - AC_AMPCAP_NUM_STEPS)) { - if (!spec->capsrc_nids) - return; /* no volume */ - if (!(query_amp_caps(codec, spec->capsrc_nids[0], HDA_OUTPUT) & - AC_AMPCAP_NUM_STEPS)) - return; /* no volume in capsrc, too */ - spec->vol_in_capsrc = 1; - } - - if (spec->num_adc_nids > 0) { - int mux = 0; - int num_adcs = 0; - - if (spec->input_mux && spec->input_mux->num_items > 1) - mux = 1; - if (spec->auto_mic) { - num_adcs = 1; - mux = 0; - } else if (spec->dyn_adc_switch) - num_adcs = 1; - if (!num_adcs) { - if (spec->num_adc_nids > 3) - spec->num_adc_nids = 3; - else if (!spec->num_adc_nids) - return; - num_adcs = spec->num_adc_nids; - } - spec->cap_mixer = caps[mux][num_adcs - 1]; - } + hda_nid_t sel = alc_go_down_to_selector(codec, pin); + if (snd_hda_get_conn_list(codec, sel, NULL) == 1) + return alc_auto_look_for_dac(codec, pin); + return 0; } -/* check whether dynamic ADC-switching is available */ -static bool alc_check_dyn_adc_switch(struct hda_codec *codec) +/* fill in the dac_nids table from the parsed pin configuration */ +static int alc_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, n, idx; - hda_nid_t cap, pin; + const struct auto_pin_cfg *cfg = &spec->autocfg; + bool redone = false; + int i; - if (imux != spec->input_mux) /* no dynamic imux? */ - return false; + again: + spec->multiout.num_dacs = 0; + spec->multiout.hp_nid = 0; + spec->multiout.extra_out_nid[0] = 0; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + spec->multiout.dac_nids = spec->private_dac_nids; - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!pin) - return false; - if (get_connection_index(codec, cap, pin) < 0) - break; - } - if (i >= imux->num_items) - return false; /* no ADC-switch is needed */ + /* fill hard-wired DACs first */ + if (!redone) { + for (i = 0; i < cfg->line_outs; i++) + spec->private_dac_nids[i] = + get_dac_if_single(codec, cfg->line_out_pins[i]); + if (cfg->hp_outs) + spec->multiout.hp_nid = + get_dac_if_single(codec, cfg->hp_pins[0]); + if (cfg->speaker_outs) + spec->multiout.extra_out_nid[0] = + get_dac_if_single(codec, cfg->speaker_pins[0]); } - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - imux->items[i].index = idx; - spec->dyn_adc_idx[i] = n; - break; - } + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t pin = cfg->line_out_pins[i]; + if (spec->private_dac_nids[i]) + continue; + spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin); + if (!spec->private_dac_nids[i] && !redone) { + /* if we can't find primary DACs, re-probe without + * checking the hard-wired DACs + */ + redone = true; + goto again; } } - snd_printdd("realtek: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - return true; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + } + + if (cfg->hp_outs && !spec->multiout.hp_nid) + spec->multiout.hp_nid = + alc_auto_look_for_dac(codec, cfg->hp_pins[0]); + if (cfg->speaker_outs && !spec->multiout.extra_out_nid[0]) + spec->multiout.extra_out_nid[0] = + alc_auto_look_for_dac(codec, cfg->speaker_pins[0]); + + return 0; } -/* filter out invalid adc_nids (and capsrc_nids) that don't give all - * active input pins - */ -static void alc_remove_invalid_adc_nids(struct hda_codec *codec) +static int alc_auto_add_vol_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) { - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - int i, n, nums; + return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); +} - imux = spec->input_mux; - if (!imux) - return; - if (spec->dyn_adc_switch) - return; +#define alc_auto_add_stereo_vol(codec, pfx, cidx, nid) \ + alc_auto_add_vol_ctl(codec, pfx, cidx, nid, 3) - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - hda_nid_t cap = spec->private_capsrc_nids[n]; - int num_conns = snd_hda_get_conn_list(codec, cap, NULL); - for (i = 0; i < imux->num_items; i++) { - hda_nid_t pin = spec->imux_pins[i]; - if (pin) { - if (get_connection_index(codec, cap, pin) < 0) - break; - } else if (num_conns <= imux->items[i].index) - break; - } - if (i >= imux->num_items) { - adc_nids[nums] = spec->private_adc_nids[n]; - capsrc_nids[nums++] = cap; - } - } - if (!nums) { - /* check whether ADC-switch is possible */ - if (!alc_check_dyn_adc_switch(codec)) { - printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", - codec->chip_name, spec->private_adc_nids[0]); - spec->num_adc_nids = 1; - spec->auto_mic = 0; - return; - } - } else if (nums != spec->num_adc_nids) { - memcpy(spec->private_adc_nids, adc_nids, - nums * sizeof(hda_nid_t)); - memcpy(spec->private_capsrc_nids, capsrc_nids, - nums * sizeof(hda_nid_t)); - spec->num_adc_nids = nums; +/* create a mute-switch for the given mixer widget; + * if it has multiple sources (e.g. DAC and loopback), create a bind-mute + */ +static int alc_auto_add_sw_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) +{ + int type; + unsigned long val; + if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); + } else { + type = ALC_CTL_BIND_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); } - - if (spec->auto_mic) - alc_auto_mic_check_imux(codec); /* check auto-mic setups */ - else if (spec->input_mux->num_items == 1) - spec->num_adc_nids = 1; /* reduce to a single ADC */ + return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); } -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) - -static const struct snd_pci_quirk beep_white_list[] = { - SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), - SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), - {} -}; +#define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \ + alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3) -static inline int has_cdefine_beep(struct hda_codec *codec) +/* add playback controls from the parsed DAC table */ +static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - q = snd_pci_quirk_lookup(codec->bus->pci, beep_white_list); - if (q) - return q->value; - return spec->cdefine.enable_pcbeep; -} -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#define has_cdefine_beep(codec) 0 -#endif - -/* - * OK, here we have finally the patch for ALC880 - */ - -static int patch_alc880(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; - - spec->mixer_nid = 0x0b; + hda_nid_t nid, mix, pin; + int i, err, noutputs; - board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST, - alc880_models, - alc880_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC880_AUTO; - } + noutputs = cfg->line_outs; + if (spec->multi_ios > 0) + noutputs += spec->multi_ios; - if (board_config == ALC880_AUTO) { - /* automatic parse from the BIOS config */ - err = alc880_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 3-stack mode...\n"); - board_config = ALC880_3ST; + for (i = 0; i < noutputs; i++) { + const char *name; + int index; + nid = spec->multiout.dac_nids[i]; + if (!nid) + continue; + if (i >= cfg->line_outs) + pin = spec->multi_io[i - 1].pin; + else + pin = cfg->line_out_pins[i]; + mix = alc_auto_dac_to_mix(codec, pin, nid); + if (!mix) + continue; + name = alc_get_line_out_pfx(spec, i, true, &index); + if (!name) { + /* Center/LFE */ + err = alc_auto_add_vol_ctl(codec, "Center", 0, nid, 1); + if (err < 0) + return err; + err = alc_auto_add_vol_ctl(codec, "LFE", 0, nid, 2); + if (err < 0) + return err; + err = alc_auto_add_sw_ctl(codec, "Center", 0, mix, 1); + if (err < 0) + return err; + err = alc_auto_add_sw_ctl(codec, "LFE", 0, mix, 2); + if (err < 0) + return err; + } else { + err = alc_auto_add_stereo_vol(codec, name, index, nid); + if (err < 0) + return err; + err = alc_auto_add_stereo_sw(codec, name, index, mix); + if (err < 0) + return err; } } + return 0; +} - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - - if (board_config != ALC880_AUTO) - setup_preset(codec, &alc880_presets[board_config]); +/* add playback controls for speaker and HP outputs */ +static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac, const char *pfx) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t mix; + int err; - if (!spec->adc_nids && spec->input_mux) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); + if (!pin) + return 0; + if (!dac) { + /* the corresponding DAC is already occupied */ + if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) + return 0; /* no way */ + /* create a switch only */ + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } - set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - - spec->vmaster_nid = 0x0c; - - codec->patch_ops = alc_patch_ops; - if (board_config == ALC880_AUTO) - spec->init_hook = alc880_auto_init; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc880_loopbacks; -#endif + mix = alc_auto_dac_to_mix(codec, pin, dac); + if (!mix) + return 0; + err = alc_auto_add_stereo_vol(codec, pfx, 0, dac); + if (err < 0) + return err; + err = alc_auto_add_stereo_sw(codec, pfx, 0, mix); + if (err < 0) + return err; return 0; } - -/* - * ALC260 support - */ - -static const hda_nid_t alc260_dac_nids[1] = { - /* front */ - 0x02, -}; - -static const hda_nid_t alc260_adc_nids[1] = { - /* ADC0 */ - 0x04, -}; - -static const hda_nid_t alc260_adc_nids_alt[1] = { - /* ADC1 */ - 0x05, -}; - -/* NIDs used when simultaneous access to both ADCs makes sense. Note that - * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. - */ -static const hda_nid_t alc260_dual_adc_nids[2] = { - /* ADC0, ADC1 */ - 0x04, 0x05 -}; - -#define ALC260_DIGOUT_NID 0x03 -#define ALC260_DIGIN_NID 0x06 - -static const struct hda_input_mux alc260_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack, - * headphone jack and the internal CD lines since these are the only pins at - * which audio can appear. For flexibility, also allow the option of - * recording the mixer output on the second ADC (ADC0 doesn't have a - * connection to the mixer output). - */ -static const struct hda_input_mux alc260_fujitsu_capture_sources[2] = { - { - .num_items = 3, - .items = { - { "Mic/Line", 0x0 }, - { "CD", 0x4 }, - { "Headphone", 0x2 }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic/Line", 0x0 }, - { "CD", 0x4 }, - { "Headphone", 0x2 }, - { "Mixer", 0x5 }, - }, - }, - -}; - -/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to - * the Fujitsu S702x, but jacks are marked differently. - */ -static const struct hda_input_mux alc260_acer_capture_sources[2] = { - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Headphone", 0x5 }, - }, - }, - { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Headphone", 0x6 }, - { "Mixer", 0x5 }, - }, - }, -}; - -/* Maxdata Favorit 100XS */ -static const struct hda_input_mux alc260_favorit100_capture_sources[2] = { - { - .num_items = 2, - .items = { - { "Line/Mic", 0x0 }, - { "CD", 0x4 }, - }, - }, - { - .num_items = 3, - .items = { - { "Line/Mic", 0x0 }, - { "CD", 0x4 }, - { "Mixer", 0x5 }, - }, - }, -}; - -/* - * This is just place-holder, so there's something for alc_build_pcms to look - * at when it calculates the maximum number of channels. ALC260 has no mixer - * element which allows changing the channel mode, so the verb list is - * never used. - */ -static const struct hda_channel_mode alc260_modes[1] = { - { 2, NULL }, -}; - - -/* Mixer combinations - * - * basic: base_output + input + pc_beep + capture - * HP: base_output + input + capture_alt - * HP_3013: hp_3013 + input + capture - * fujitsu: fujitsu + capture - * acer: acer + capture - */ - -static const struct snd_kcontrol_new alc260_base_output_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc260_input_mixer[] = { - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT), - { } /* end */ -}; - -/* update HP, line and mono out pins according to the master switch */ -static void alc260_hp_master_update(struct hda_codec *codec) +static int alc_auto_create_hp_out(struct hda_codec *codec) { - update_speakers(codec); + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + spec->multiout.hp_nid, + "Headphone"); } -static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int alc_auto_create_speaker_out(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - *ucontrol->value.integer.value = !spec->master_mute; - return 0; + return alc_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], + spec->multiout.extra_out_nid[0], + "Speaker"); } -static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + hda_nid_t dac) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int val = !*ucontrol->value.integer.value; - - if (val == spec->master_mute) - return 0; - spec->master_mute = val; - alc260_hp_master_update(codec); - return 1; -} + int i, num; + hda_nid_t mix = 0; + hda_nid_t srcs[HDA_MAX_CONNECTIONS]; -static const struct snd_kcontrol_new alc260_hp_output_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, - .info = snd_ctl_boolean_mono_info, - .get = alc260_hp_master_sw_get, - .put = alc260_hp_master_sw_put, - }, - HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), - { } /* end */ -}; + alc_set_pin_output(codec, nid, pin_type); + nid = alc_go_down_to_selector(codec, nid); + num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) + continue; + mix = srcs[i]; + break; + } + if (!mix) + return; -static const struct hda_verb alc260_hp_unsol_verbs[] = { - {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {}, -}; + /* need the manual connection? */ + if (num > 1) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); + /* unmute mixer widget inputs */ + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + /* initialize volume */ + if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = dac; + else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) + nid = mix; + else + return; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); +} -static void alc260_hp_setup(struct hda_codec *codec) +static void alc_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + int pin_type = get_pin_type(spec->autocfg.line_out_type); + int i; - spec->autocfg.hp_pins[0] = 0x0f; - spec->autocfg.speaker_pins[0] = 0x10; - spec->autocfg.speaker_pins[1] = 0x11; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + alc_auto_set_output_and_unmute(codec, nid, pin_type, + spec->multiout.dac_nids[i]); + } } -static const struct snd_kcontrol_new alc260_hp_3013_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, - .info = snd_ctl_boolean_mono_info, - .get = alc260_hp_master_sw_get, - .put = alc260_hp_master_sw_put, - }, - HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static void alc260_hp_3013_setup(struct hda_codec *codec) +static void alc_auto_init_extra_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + hda_nid_t pin; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x10; - spec->autocfg.speaker_pins[1] = 0x11; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct hda_bind_ctls alc260_dc7600_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc260_dc7600_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol), - HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb alc260_hp_3013_unsol_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {}, -}; - -static void alc260_hp_3012_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x10; - spec->autocfg.speaker_pins[0] = 0x0f; - spec->autocfg.speaker_pins[1] = 0x11; - spec->autocfg.speaker_pins[2] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, - * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. - */ -static const struct snd_kcontrol_new alc260_fujitsu_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT), - ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT), - { } /* end */ -}; - -/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current - * versions of the ALC260 don't act on requests to enable mic bias from NID - * 0x0f (used to drive the headphone jack in these laptops). The ALC260 - * datasheet doesn't mention this restriction. At this stage it's not clear - * whether this behaviour is intentional or is a hardware bug in chip - * revisions available in early 2006. Therefore for now allow the - * "Headphone Jack Mode" control to span all choices, but if it turns out - * that the lack of mic bias for this NID is intentional we could change the - * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. - * - * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006 - * don't appear to make the mic bias available from the "line" jack, even - * though the NID used for this jack (0x14) can supply it. The theory is - * that perhaps Acer have included blocking capacitors between the ALC260 - * and the output jack. If this turns out to be the case for all such - * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT - * to ALC_PIN_DIR_INOUT_NOMICBIAS. - * - * The C20x Tablet series have a mono internal speaker which is controlled - * via the chip's Mono sum widget and pin complex, so include the necessary - * controls for such models. On models without a "mono speaker" the control - * won't do anything. - */ -static const struct snd_kcontrol_new alc260_acer_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), - ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, - HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - { } /* end */ -}; - -/* Maxdata Favorit 100XS: one output and one input (0x12) jack - */ -static const struct snd_kcontrol_new alc260_favorit100_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), - ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - { } /* end */ -}; - -/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12, - * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17. - */ -static const struct snd_kcontrol_new alc260_will_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - { } /* end */ -}; - -/* Replacer 672V ALC260 pin usage: Mic jack = 0x12, - * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f. - */ -static const struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - { } /* end */ -}; - -/* - * initialization verbs - */ -static const struct hda_verb alc260_init_verbs[] = { - /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* LINE-2 is used for line-out in rear */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* select line-out */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* LINE-OUT pin */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* enable HP */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* enable Mono */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* set connection select to line in (default select for this ADC) */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* mute capture amp left and right */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* set connection select to line in (default select for this ADC) */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* set vol=0 Line-Out mixer amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* unmute pin widget amp left and right (no gain on this amp) */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* set vol=0 HP mixer amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* unmute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* set vol=0 Mono mixer amp left and right */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* unmute pin widget amp left and right (no gain on this amp) */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* unmute LINE-2 out pin */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & - * Line In 2 = 0x03 - */ - /* mute analog inputs */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* mute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* mute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* mute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; - -#if 0 /* should be identical with alc260_init_verbs? */ -static const struct hda_verb alc260_hp_init_verbs[] = { - /* Headphone and output */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - /* mono output */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* Line-2 pin widget for output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* unmute amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* set connection select to line in (default select for this ADC) */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute Line-Out mixer amp left and right (volume = 0) */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute HP mixer amp left and right (volume = 0) */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & - * Line In 2 = 0x03 - */ - /* mute analog inputs */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* Unmute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - { } -}; -#endif - -static const struct hda_verb alc260_hp_3013_init_verbs[] = { - /* Line out and output */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* mono output */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* Headphone pin widget for output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* unmute amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* set connection select to line in (default select for this ADC) */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute Line-Out mixer amp left and right (volume = 0) */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute HP mixer amp left and right (volume = 0) */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & - * Line In 2 = 0x03 - */ - /* mute analog inputs */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* Unmute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - { } -}; - -/* Initialisation sequence for ALC260 as configured in Fujitsu S702x - * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD - * audio = 0x16, internal speaker = 0x10. - */ -static const struct hda_verb alc260_fujitsu_init_verbs[] = { - /* Disable all GPIOs */ - {0x01, AC_VERB_SET_GPIO_MASK, 0}, - /* Internal speaker is connected to headphone pin */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Headphone/Line-out jack connects to Line1 pin; make it an output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mic/Line-in jack is connected to mic1 pin, so make it an input */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Ensure all other unused pins are disabled and muted. */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - /* Disable digital (SPDIF) pins */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure Line1 pin widget takes its input from the OUT1 sum bus - * when acting as an output. - */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Line1 pin widget output buffer since it starts as an output. - * If the pin mode is changed by the user the pin mode control will - * take care of enabling the pin's input/output buffers as needed. - * Therefore there's no need to enable the input buffer at this - * stage. - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute input buffer of pin widget used for Line-in (no equiv - * mixer ctrl) - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting - line - * in (on mic1 pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do the same for the second ADC: mute capture input amp and - * set ADC connection to line in (on mic1 pin) - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; - -/* Initialisation sequence for ALC260 as configured in Acer TravelMate and - * similar laptops (adapted from Fujitsu init verbs). - */ -static const struct hda_verb alc260_acer_init_verbs[] = { - /* On TravelMate laptops, GPIO 0 enables the internal speaker and - * the headphone jack. Turn this on and rely on the standard mute - * methods whenever the user wants to turn these outputs off. - */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - /* Internal speaker/Headphone jack is connected to Line-out pin */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Internal microphone/Mic jack is connected to Mic1 pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - /* Line In jack is connected to Line1 pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Ensure all other unused pins are disabled and muted. */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Disable digital (SPDIF) pins */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum - * bus when acting as outputs. - */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute Line-out pin widget amp left and right - * (no equiv mixer ctrl) - */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute mono pin widget amp output (no equiv mixer ctrl) */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Mic1 and Line1 pin widget input buffers since they start as - * inputs. If the pin mode is changed by the user the pin mode control - * will take care of enabling the pin's input/output buffers as needed. - * Therefore there's no need to enable the input buffer at this - * stage. - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting - mic - * (on mic1 pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do similar with the second ADC: mute capture input amp and - * set ADC connection to mic to match ALSA's default state. - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; - -/* Initialisation sequence for Maxdata Favorit 100XS - * (adapted from Acer init verbs). - */ -static const struct hda_verb alc260_favorit100_init_verbs[] = { - /* GPIO 0 enables the output jack. - * Turn this on and rely on the standard mute - * methods whenever the user wants to turn these outputs off. - */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - /* Line/Mic input jack is connected to Mic1 pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - /* Ensure all other unused pins are disabled and muted. */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Disable digital (SPDIF) pins */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum - * bus when acting as outputs. - */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute Line-out pin widget amp left and right - * (no equiv mixer ctrl) - */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Mic1 and Line1 pin widget input buffers since they start as - * inputs. If the pin mode is changed by the user the pin mode control - * will take care of enabling the pin's input/output buffers as needed. - * Therefore there's no need to enable the input buffer at this - * stage. - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting - mic - * (on mic1 pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do similar with the second ADC: mute capture input amp and - * set ADC connection to mic to match ALSA's default state. - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; - -static const struct hda_verb alc260_will_verbs[] = { - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x1a, AC_VERB_SET_PROC_COEF, 0x3040}, - {} -}; - -static const struct hda_verb alc260_replacer_672v_verbs[] = { - {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x1a, AC_VERB_SET_PROC_COEF, 0x3050}, - - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - - {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc260_replacer_672v_automute(struct hda_codec *codec) -{ - unsigned int present; - - /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ - present = snd_hda_jack_detect(codec, 0x0f); - if (present) { - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 1); - snd_hda_codec_write_cache(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_HP); - } else { - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0); - snd_hda_codec_write_cache(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - } -} - -static void alc260_replacer_672v_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc260_replacer_672v_automute(codec); + pin = spec->autocfg.hp_pins[0]; + if (pin) + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, + spec->multiout.hp_nid); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, + spec->multiout.extra_out_nid[0]); } -static const struct hda_verb alc260_hp_dc7600_verbs[] = { - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -/* Test configuration for debugging, modelled after the ALC880 test - * configuration. - */ -#ifdef CONFIG_SND_DEBUG -static const hda_nid_t alc260_test_dac_nids[1] = { - 0x02, -}; -static const hda_nid_t alc260_test_adc_nids[2] = { - 0x04, 0x05, -}; -/* For testing the ALC260, each input MUX needs its own definition since - * the signal assignments are different. This assumes that the first ADC - * is NID 0x04. - */ -static const struct hda_input_mux alc260_test_capture_sources[2] = { - { - .num_items = 7, - .items = { - { "MIC1 pin", 0x0 }, - { "MIC2 pin", 0x1 }, - { "LINE1 pin", 0x2 }, - { "LINE2 pin", 0x3 }, - { "CD pin", 0x4 }, - { "LINE-OUT pin", 0x5 }, - { "HP-OUT pin", 0x6 }, - }, - }, - { - .num_items = 8, - .items = { - { "MIC1 pin", 0x0 }, - { "MIC2 pin", 0x1 }, - { "LINE1 pin", 0x2 }, - { "LINE2 pin", 0x3 }, - { "CD pin", 0x4 }, - { "Mixer", 0x5 }, - { "LINE-OUT pin", 0x6 }, - { "HP-OUT pin", 0x7 }, - }, - }, -}; -static const struct snd_kcontrol_new alc260_test_mixer[] = { - /* Output driver widgets */ - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), - HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT), - HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT), - - /* Modes for retasking pin widgets - * Note: the ALC260 doesn't seem to act on requests to enable mic - * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't - * mention this restriction. At this stage it's not clear whether - * this behaviour is intentional or is a hardware bug in chip - * revisions available at least up until early 2006. Therefore for - * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all - * choices, but if it turns out that the lack of mic bias for these - * NIDs is intentional we could change their modes from - * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. - */ - ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT), - - /* Loopback mixer controls */ - HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT), - HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT), - - /* Controls for GPIO pins, assuming they are configured as outputs */ - ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), - ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), - ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), - ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), - - /* Switches to allow the digital IO pins to be enabled. The datasheet - * is ambigious as to which NID is which; testing on laptops which - * make this output available should provide clarification. - */ - ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01), - ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), - - /* A switch allowing EAPD to be enabled. Some laptops seem to use - * this output to turn on an external amplifier. - */ - ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), - ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), - - { } /* end */ -}; -static const struct hda_verb alc260_test_init_verbs[] = { - /* Enable all GPIOs as outputs with an initial value of 0 */ - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - {0x01, AC_VERB_SET_GPIO_MASK, 0x0f}, - - /* Enable retasking pins as output, initially without power amp */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* Disable digital (SPDIF) pins initially, but users can enable - * them via a mixer switch. In the case of SPDIF-out, this initverb - * payload also sets the generation to 0, output to be in "consumer" - * PCM format, copyright asserted, no pre-emphasis and no validity - * control. - */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the - * OUT1 sum bus when acting as an output. - */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0c, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute retasking pin widget output buffers since the default - * state appears to be output. As the pin mode is changed by the - * user the pin mode control will take care of enabling the pin's - * input/output buffers as needed. - */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Also unmute the mono-out pin widget */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting (mic1 - * pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do the same for the second ADC: mute capture input amp and - * set ADC connection to mic1 pin - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; -#endif - /* - * for BIOS auto-configuration + * multi-io helper */ - -/* convert from pin to volume-mixer widget */ -static hda_nid_t alc260_pin_to_vol_mix(hda_nid_t nid) -{ - if (nid >= 0x0f && nid <= 0x11) - return nid - 0x7; - else if (nid >= 0x12 && nid <= 0x15) - return 0x08; - else - return 0; -} - -static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vol_bits) +static int alc_auto_fill_multi_ios(struct hda_codec *codec, + unsigned int location) { - hda_nid_t nid_vol; - unsigned long vol_val, sw_val; - int chs, err; - - nid_vol = alc260_pin_to_vol_mix(nid); - if (!nid_vol) - return 0; /* N/A */ - if (nid == 0x11) - chs = 2; - else - chs = 3; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, chs, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int type, i, num_pins = 0; - if (!(*vol_bits & (1 << nid_vol))) { - /* first control for the volume widget */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); - if (err < 0) - return err; - *vol_bits |= (1 << nid_vol); + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + hda_nid_t dac; + unsigned int defcfg, caps; + if (cfg->inputs[i].type != type) + continue; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) + continue; + if (location && get_defcfg_location(defcfg) != location) + continue; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + continue; + dac = alc_auto_look_for_dac(codec, nid); + if (!dac) + continue; + spec->multi_io[num_pins].pin = nid; + spec->multi_io[num_pins].dac = dac; + num_pins++; + spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + } } - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); - if (err < 0) - return err; - return 1; -} - -/* add playback controls from the parsed DAC table */ -static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int err; - int vols = 0; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - spec->private_dac_nids[0] = 0x02; - - nid = cfg->line_out_pins[0]; - if (nid) { - const char *pfx; - int index; - pfx = alc_get_line_out_pfx(spec, 0, true, &index); - err = alc260_add_playback_controls(spec, nid, pfx, &vols); - if (err < 0) - return err; - } - - nid = cfg->speaker_pins[0]; - if (nid) { - err = alc260_add_playback_controls(spec, nid, "Speaker", &vols); - if (err < 0) - return err; - } - - nid = cfg->hp_pins[0]; - if (nid) { - err = alc260_add_playback_controls(spec, nid, "Headphone", - &vols); - if (err < 0) - return err; - } - return 0; -} - -static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int sel_idx) -{ - hda_nid_t mix; - - alc_set_pin_output(codec, nid, pin_type); - /* need the manual connection? */ - if (nid >= 0x12) { - int idx = nid - 0x12; - snd_hda_codec_write(codec, idx + 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, sel_idx); - } - - mix = alc260_pin_to_vol_mix(nid); - if (!mix) - return; - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); -} - -static void alc260_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - - nid = spec->autocfg.line_out_pins[0]; - if (nid) { - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0); - } - - nid = spec->autocfg.speaker_pins[0]; - if (nid) - alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); - - nid = spec->autocfg.hp_pins[0]; - if (nid) - alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); -} - -static int alc260_parse_auto_config(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int err; - static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc260_ignore); - if (err < 0) - return err; - err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->kctls.list) - return 0; /* can't find valid BIOS pin config */ - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); - alc_auto_check_switches(codec); - - return 1; -} - -/* additional initialization for auto-configuration model */ -static void alc260_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc260_auto_init_multi_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc260_loopbacks[] = { - { 0x07, HDA_INPUT, 0 }, - { 0x07, HDA_INPUT, 1 }, - { 0x07, HDA_INPUT, 2 }, - { 0x07, HDA_INPUT, 3 }, - { 0x07, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - -/* - * Pin config fixes - */ -enum { - PINFIX_HP_DC5750, -}; - -static const struct alc_fixup alc260_fixups[] = { - [PINFIX_HP_DC5750] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x11, 0x90130110 }, /* speaker */ - { } - } - }, -}; - -static const struct snd_pci_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750), - {} -}; - -/* - * ALC260 configurations - */ -static const char * const alc260_models[ALC260_MODEL_LAST] = { - [ALC260_BASIC] = "basic", - [ALC260_HP] = "hp", - [ALC260_HP_3013] = "hp-3013", - [ALC260_HP_DC7600] = "hp-dc7600", - [ALC260_FUJITSU_S702X] = "fujitsu", - [ALC260_ACER] = "acer", - [ALC260_WILL] = "will", - [ALC260_REPLACER_672V] = "replacer", - [ALC260_FAVORIT100] = "favorit100", -#ifdef CONFIG_SND_DEBUG - [ALC260_TEST] = "test", -#endif - [ALC260_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc260_cfg_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), - SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL), - SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), - SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100), - SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */ - SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600), - SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP), - SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), - SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP), - SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC), - SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC), - SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), - SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), - SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), - SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), - {} -}; - -static const struct alc_config_preset alc260_presets[] = { - [ALC260_BASIC] = { - .mixers = { alc260_base_output_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - }, - [ALC260_HP] = { - .mixers = { alc260_hp_output_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_init_verbs, - alc260_hp_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), - .adc_nids = alc260_adc_nids_alt, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc260_hp_setup, - .init_hook = alc_inithook, - }, - [ALC260_HP_DC7600] = { - .mixers = { alc260_hp_dc7600_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_init_verbs, - alc260_hp_dc7600_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), - .adc_nids = alc260_adc_nids_alt, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc260_hp_3012_setup, - .init_hook = alc_inithook, - }, - [ALC260_HP_3013] = { - .mixers = { alc260_hp_3013_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_hp_3013_init_verbs, - alc260_hp_3013_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), - .adc_nids = alc260_adc_nids_alt, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc260_hp_3013_setup, - .init_hook = alc_inithook, - }, - [ALC260_FUJITSU_S702X] = { - .mixers = { alc260_fujitsu_mixer }, - .init_verbs = { alc260_fujitsu_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources), - .input_mux = alc260_fujitsu_capture_sources, - }, - [ALC260_ACER] = { - .mixers = { alc260_acer_mixer }, - .init_verbs = { alc260_acer_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), - .input_mux = alc260_acer_capture_sources, - }, - [ALC260_FAVORIT100] = { - .mixers = { alc260_favorit100_mixer }, - .init_verbs = { alc260_favorit100_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources), - .input_mux = alc260_favorit100_capture_sources, - }, - [ALC260_WILL] = { - .mixers = { alc260_will_mixer }, - .init_verbs = { alc260_init_verbs, alc260_will_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), - .adc_nids = alc260_adc_nids, - .dig_out_nid = ALC260_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - }, - [ALC260_REPLACER_672V] = { - .mixers = { alc260_replacer_672v_mixer }, - .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), - .adc_nids = alc260_adc_nids, - .dig_out_nid = ALC260_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc260_replacer_672v_unsol_event, - .init_hook = alc260_replacer_672v_automute, - }, -#ifdef CONFIG_SND_DEBUG - [ALC260_TEST] = { - .mixers = { alc260_test_mixer }, - .init_verbs = { alc260_test_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), - .dac_nids = alc260_test_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids), - .adc_nids = alc260_test_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources), - .input_mux = alc260_test_capture_sources, - }, -#endif -}; - -static int patch_alc260(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x07; - - board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST, - alc260_models, - alc260_cfg_tbl); - if (board_config < 0) { - snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC260_AUTO; - } - - if (board_config == ALC260_AUTO) { - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - if (board_config == ALC260_AUTO) { - /* automatic parse from the BIOS config */ - err = alc260_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 = ALC260_BASIC; - } - } - - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - - if (board_config != ALC260_AUTO) - setup_preset(codec, &alc260_presets[board_config]); - - if (!spec->adc_nids && spec->input_mux) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - set_capture_mixer(codec); - set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - spec->vmaster_nid = 0x08; - - codec->patch_ops = alc_patch_ops; - if (board_config == ALC260_AUTO) - spec->init_hook = alc260_auto_init; - spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc260_loopbacks; -#endif - - return 0; -} - - -/* - * ALC882/883/885/888/889 support - * - * ALC882 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ -#define ALC882_DIGOUT_NID 0x06 -#define ALC882_DIGIN_NID 0x0a -#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID -#define ALC883_DIGIN_NID ALC882_DIGIN_NID -#define ALC1200_DIGOUT_NID 0x10 - - -static const struct hda_channel_mode alc882_ch_modes[1] = { - { 8, NULL } -}; - -/* DACs */ -static const hda_nid_t alc882_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x03, 0x04, 0x05 -}; -#define alc883_dac_nids alc882_dac_nids - -/* ADCs */ -#define alc882_adc_nids alc880_adc_nids -#define alc882_adc_nids_alt alc880_adc_nids_alt -#define alc883_adc_nids alc882_adc_nids_alt -static const hda_nid_t alc883_adc_nids_alt[1] = { 0x08 }; -static const hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 }; -#define alc889_adc_nids alc880_adc_nids - -static const hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; -static const hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; -#define alc883_capsrc_nids alc882_capsrc_nids_alt -static const hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; -#define alc889_capsrc_nids alc882_capsrc_nids - -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ - -static const struct hda_input_mux alc882_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -#define alc883_capture_source alc882_capture_source - -static const struct hda_input_mux alc889_capture_source = { - .num_items = 3, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x3 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux mb5_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x1 }, - { "Line", 0x7 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux macmini3_capture_source = { - .num_items = 2, - .items = { - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_3stack_6ch_intel = { - .num_items = 4, - .items = { - { "Mic", 0x1 }, - { "Front Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_lenovo_101e_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux alc883_lenovo_nb0763_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; - -static const struct hda_input_mux alc883_lenovo_sky_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_asus_eee1601_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux alc889A_mb31_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - /* Front Mic (0x01) unused */ - { "Line", 0x2 }, - /* Line 2 (0x03) unused */ - /* CD (0x04) unused? */ - }, -}; - -static const struct hda_input_mux alc889A_imac91_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x01 }, - { "Line", 0x2 }, /* Not sure! */ - }, -}; - -/* - * 2ch mode - */ -static const struct hda_channel_mode alc883_3ST_2ch_modes[1] = { - { 2, NULL } -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc882_3ST_ch2_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc882_3ST_ch4_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc882_3ST_ch6_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc882_3ST_6ch_modes[3] = { - { 2, alc882_3ST_ch2_init }, - { 4, alc882_3ST_ch4_init }, - { 6, alc882_3ST_ch6_init }, -}; - -#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes - -/* - * 2ch mode - */ -static const struct hda_verb alc883_3ST_ch2_clevo_init[] = { - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc883_3ST_ch4_clevo_init[] = { - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_3ST_ch6_clevo_init[] = { - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = { - { 2, alc883_3ST_ch2_clevo_init }, - { 4, alc883_3ST_ch4_clevo_init }, - { 6, alc883_3ST_ch6_clevo_init }, -}; - - -/* - * 6ch mode - */ -static const struct hda_verb alc882_sixstack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc882_sixstack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -static const struct hda_channel_mode alc882_sixstack_modes[2] = { - { 6, alc882_sixstack_ch6_init }, - { 8, alc882_sixstack_ch8_init }, -}; - - -/* Macbook Air 2,1 */ - -static const struct hda_channel_mode alc885_mba21_ch_modes[1] = { - { 2, NULL }, -}; - -/* - * macbook pro ALC885 can switch LineIn to LineOut without losing Mic - */ - -/* - * 2ch mode - */ -static const struct hda_verb alc885_mbp_ch2_init[] = { - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc885_mbp_ch4_init[] = { - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } /* end */ -}; - -static const struct hda_channel_mode alc885_mbp_4ch_modes[2] = { - { 2, alc885_mbp_ch2_init }, - { 4, alc885_mbp_ch4_init }, -}; - -/* - * 2ch - * Speakers/Woofer/HP = Front - * LineIn = Input - */ -static const struct hda_verb alc885_mb5_ch2_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - { } /* end */ -}; - -/* - * 6ch mode - * Speakers/HP = Front - * Woofer = LFE - * LineIn = Surround - */ -static const struct hda_verb alc885_mb5_ch6_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - { } /* end */ -}; - -static const struct hda_channel_mode alc885_mb5_6ch_modes[2] = { - { 2, alc885_mb5_ch2_init }, - { 6, alc885_mb5_ch6_init }, -}; - -#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes - -/* - * 2ch mode - */ -static const struct hda_verb alc883_4ST_ch2_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc883_4ST_ch4_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_4ST_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc883_4ST_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_4ST_8ch_modes[4] = { - { 2, alc883_4ST_ch2_init }, - { 4, alc883_4ST_ch4_init }, - { 6, alc883_4ST_ch6_init }, - { 8, alc883_4ST_ch8_init }, -}; - - -/* - * 2ch mode - */ -static const struct hda_verb alc883_3ST_ch2_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc883_3ST_ch4_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_3ST_ch6_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { - { 2, alc883_3ST_ch2_intel_init }, - { 4, alc883_3ST_ch4_intel_init }, - { 6, alc883_3ST_ch6_intel_init }, -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc889_ch2_intel_init[] = { - { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc889_ch6_intel_init[] = { - { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc889_ch8_intel_init[] = { - { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode alc889_8ch_intel_modes[3] = { - { 2, alc889_ch2_intel_init }, - { 6, alc889_ch6_intel_init }, - { 8, alc889_ch8_intel_init }, -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_sixstack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc883_sixstack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_sixstack_modes[2] = { - { 6, alc883_sixstack_ch6_init }, - { 8, alc883_sixstack_ch8_init }, -}; - - -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ -static const struct snd_kcontrol_new alc882_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -/* Macbook Air 2,1 same control for HP and internal Speaker */ - -static const struct snd_kcontrol_new alc885_mba21_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT), - { } -}; - - -static const struct snd_kcontrol_new alc885_mbp3_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, 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, 0x00, HDA_INPUT), - HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_mb5_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0x00, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_macmini3_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_imac91_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), - { } /* end */ -}; - - -static const struct snd_kcontrol_new alc882_w2jc_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc882_targa_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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 Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ??? - * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c - */ -static const struct snd_kcontrol_new alc882_asus_a7j_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, 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 Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc882_asus_a7m_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc882_chmode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc882_base_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Rear Pin: output 1 (0x0d) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* Side Pin: output 3 (0x0f) */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line-2 In: Headphone output (output 0 - 0x0c) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* ADC2: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - { } -}; - -static const struct hda_verb alc882_adc1_init_verbs[] = { - /* 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(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* ADC1: mute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } -}; - -static const struct hda_verb alc882_eapd_verbs[] = { - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - { } -}; - -static const struct hda_verb alc889_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc_hp15_unsol_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc885_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Front HP Pin: output 0 (0x0c) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Rear Pin: output 1 (0x0d) */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* Side Pin: output 3 (0x0f) */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic (rear) pin: input vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - /* Mixer elements: 0x18, , 0x1a, 0x1b */ - /* Input mixer1 */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* ADC2: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - { } -}; - -static const struct hda_verb alc885_init_input_verbs[] = { - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - { } -}; - - -/* Unmute Selector 24h and set the default input to front mic */ -static const struct hda_verb alc889_init_input_verbs[] = { - {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - { } -}; - - -#define alc883_init_verbs alc882_base_init_verbs - -/* Mac Pro test */ -static const struct snd_kcontrol_new alc882_macpro_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), - /* FIXME: this looks suspicious... - HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT), - */ - { } /* end */ -}; - -static const struct hda_verb alc882_macpro_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin: output 0 (0x0c) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Speaker: output */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04}, - /* Headphone output (output 0 - 0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 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(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* ADC1: mute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC2: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - { } -}; - -/* Macbook 5,1 */ -static const struct hda_verb alc885_mb5_init_verbs[] = { - /* DACs */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Front mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Surround mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* LFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* HP mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* LFE Pin (0x0e) */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* HP Pin (0x0f) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0x1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x7)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x4)}, - { } -}; - -/* Macmini 3,1 */ -static const struct hda_verb alc885_macmini3_init_verbs[] = { - /* DACs */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Front mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Surround mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* LFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* HP mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* LFE Pin (0x0e) */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* HP Pin (0x0f) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* Line In pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - { } -}; - - -static const struct hda_verb alc885_mba21_init_verbs[] = { - /*Internal and HP Speaker Mixer*/ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /*Internal Speaker Pin (0x0c)*/ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP Pin: output 0 (0x0e) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)}, - /* Line in (is hp when jack connected)*/ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - { } - }; - - -/* Macbook Pro rev3 */ -static const struct hda_verb alc885_mbp3_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* HP mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP Pin: output 0 (0x0e) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: use output 1 when in LineOut mode */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 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(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* ADC1: mute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC2: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - { } -}; - -/* iMac 9,1 */ -static const struct hda_verb alc885_imac91_init_verbs[] = { - /* Internal Speaker Pin (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP Pin: Rear */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)}, - /* Line in Rear */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* 0x24 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* 0x23 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* 0x22 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* 0x07 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* 0x08 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* 0x09 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } -}; - -/* iMac 24 mixer. */ -static const struct snd_kcontrol_new alc885_imac24_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT), - { } /* end */ -}; - -/* iMac 24 init verbs. */ -static const struct hda_verb alc885_imac24_init_verbs[] = { - /* Internal speakers: output 0 (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Internal speakers: output 0 (0x0c) */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Headphone: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* Front Mic: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - { } -}; - -/* Toggle speaker-output according to the hp-jack state */ -static void alc885_imac24_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x18; - spec->autocfg.speaker_pins[1] = 0x1a; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -#define alc885_mb5_setup alc885_imac24_setup -#define alc885_macmini3_setup alc885_imac24_setup - -/* Macbook Air 2,1 */ -static void alc885_mba21_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x18; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - - - -static void alc885_mbp3_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc885_imac91_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x18; - spec->autocfg.speaker_pins[1] = 0x1a; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc882_targa_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc882_targa_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_hp_automute(codec); - snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, - spec->jack_present ? 1 : 3); -} - -static void alc882_targa_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc882_targa_automute(codec); -} - -static const struct hda_verb alc882_asus_a7j_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - { } /* end */ -}; - -static const struct hda_verb alc882_asus_a7m_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - { } /* end */ -}; - -static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) -{ - unsigned int gpiostate, gpiomask, gpiodir; - - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); - - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); - - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); - - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); - - msleep(1); - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); -} - -/* set up GPIO at initialization */ -static void alc885_macpro_init_hook(struct hda_codec *codec) -{ - alc882_gpio_mute(codec, 0, 0); - alc882_gpio_mute(codec, 1, 0); -} - -/* set up GPIO and update auto-muting at initialization */ -static void alc885_imac24_init_hook(struct hda_codec *codec) -{ - alc885_macpro_init_hook(codec); - alc_hp_automute(codec); -} - -/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */ -static const struct hda_verb alc889A_mb31_ch2_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ - { } /* end */ -}; - -/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */ -static const struct hda_verb alc889A_mb31_ch4_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ - { } /* end */ -}; - -/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */ -static const struct hda_verb alc889A_mb31_ch5_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ - { } /* end */ -}; - -/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */ -static const struct hda_verb alc889A_mb31_ch6_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ - { } /* end */ -}; - -static const struct hda_channel_mode alc889A_mb31_6ch_modes[4] = { - { 2, alc889A_mb31_ch2_init }, - { 4, alc889A_mb31_ch4_init }, - { 5, alc889A_mb31_ch5_init }, - { 6, alc889A_mb31_ch6_init }, -}; - -static const struct hda_verb alc883_medion_eapd_verbs[] = { - /* eanable EAPD on medion laptop */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, - { } -}; - -#define alc883_base_mixer alc882_base_mixer - -static const struct snd_kcontrol_new alc883_mitac_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_8ch_intel_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x1b, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_fivestack_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_targa_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_targa_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_targa_8ch_mixer[] = { - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, 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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_medion_wim2160_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc883_medion_wim2160_verbs[] = { - /* Unmute front mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Set speaker pin to front mixer */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Init headphone pin */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_medion_wim2160_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1a; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", - 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc889A_mb31_mixer[] = { - /* Output mixers */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT), - /* Output switches */ - HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT), - /* Boost mixers */ - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT), - /* Input mixers */ - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_vaiott_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_bind_ctls alc883_bind_cap_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc883_bind_cap_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = { - HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), - HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_chmode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_mitac_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc883_mitac_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Subwoofer */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* enable unsolicited event */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */ - - { } /* end */ -}; - -static const struct hda_verb alc883_clevo_m540r_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Int speaker */ - /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/ - - /* enable unsolicited event */ - /* - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - */ - - { } /* end */ -}; - -static const struct hda_verb alc883_clevo_m720_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Int speaker */ - {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* enable unsolicited event */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static const struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { - /* HP */ - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Subwoofer */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* enable unsolicited event */ - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static const struct hda_verb alc883_targa_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - -/* Connect Line-Out side jack (SPDIF) to Side */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, -/* Connect Mic jack to CLFE */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, -/* Connect Line-in jack to Surround */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, -/* Connect HP out jack to Front */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static const struct hda_verb alc883_lenovo_101e_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN}, - { } /* end */ -}; - -static const struct hda_verb alc883_lenovo_nb0763_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - { } /* end */ -}; - -static const struct hda_verb alc888_lenovo_ms7195_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT | AC_USRSP_EN}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static const struct hda_verb alc883_haier_w66_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - { } /* end */ -}; - -static const struct hda_verb alc888_lenovo_sky_verbs[] = { - {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)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static const struct hda_verb alc888_6st_dell_verbs[] = { - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -static const struct hda_verb alc883_vaiott_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - /* enable unsolicited event */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static void alc888_3st_hp_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x18; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc888_3st_hp_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc888_3st_hp_2ch_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc888_3st_hp_4ch_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc888_3st_hp_6ch_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc888_3st_hp_modes[3] = { - { 2, alc888_3st_hp_2ch_init }, - { 4, alc888_3st_hp_4ch_init }, - { 6, alc888_3st_hp_6ch_init }, -}; - -static void alc888_lenovo_ms7195_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.line_out_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_lenovo_nb0763_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* toggle speaker-output according to the hp-jack state */ -#define alc883_targa_init_hook alc882_targa_init_hook -#define alc883_targa_unsol_event alc882_targa_unsol_event - -static void alc883_clevo_m720_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_clevo_m720_init_hook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc88x_simple_mic_automute(codec); -} - -static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_haier_w66_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_lenovo_101e_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.line_out_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->detect_line = 1; - spec->automute_lines = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_acer_aspire_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc883_acer_eapd_verbs[] = { - /* HP Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Front Pin: output 0 (0x0c) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* eanable EAPD on medion laptop */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, - /* enable unsolicited event */ - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -static void alc888_6st_dell_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.speaker_pins[2] = 0x16; - spec->autocfg.speaker_pins[3] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc888_lenovo_sky_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.speaker_pins[2] = 0x16; - spec->autocfg.speaker_pins[3] = 0x17; - spec->autocfg.speaker_pins[4] = 0x1a; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_vaiott_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc888_asus_m90v_verbs[] = { - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* enable unsolicited event */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static void alc883_mode2_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.speaker_pins[2] = 0x16; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc888_asus_eee1601_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0838}, - /* enable unsolicited event */ - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static void alc883_eee1601_inithook(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - alc_hp_automute(codec); -} - -static const struct hda_verb alc889A_mb31_verbs[] = { - /* Init rear pin (used as headphone output) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* Init line pin (used as output in 4ch and 6ch mode) */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */ - /* Init line 2 pin (used as headphone out by default) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */ - { } /* end */ -}; - -/* Mute speakers according to the headphone jack state */ -static void alc889A_mb31_automute(struct hda_codec *codec) -{ - unsigned int present; - - /* Mute only in 2ch or 4ch mode */ - if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0) - == 0x00) { - present = snd_hda_jack_detect(codec, 0x15); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - } -} - -static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc889A_mb31_automute(codec); -} - - -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc882_loopbacks alc880_loopbacks -#endif - -static const hda_nid_t alc883_slave_dig_outs[] = { - ALC1200_DIGOUT_NID, 0, -}; - -static const hda_nid_t alc1200_slave_dig_outs[] = { - ALC883_DIGOUT_NID, 0, -}; - -/* - * configuration and preset - */ -static const char * const alc882_models[ALC882_MODEL_LAST] = { - [ALC882_3ST_DIG] = "3stack-dig", - [ALC882_6ST_DIG] = "6stack-dig", - [ALC882_ARIMA] = "arima", - [ALC882_W2JC] = "w2jc", - [ALC882_TARGA] = "targa", - [ALC882_ASUS_A7J] = "asus-a7j", - [ALC882_ASUS_A7M] = "asus-a7m", - [ALC885_MACPRO] = "macpro", - [ALC885_MB5] = "mb5", - [ALC885_MACMINI3] = "macmini3", - [ALC885_MBA21] = "mba21", - [ALC885_MBP3] = "mbp3", - [ALC885_IMAC24] = "imac24", - [ALC885_IMAC91] = "imac91", - [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig", - [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", - [ALC883_3ST_6ch] = "3stack-6ch", - [ALC883_6ST_DIG] = "alc883-6stack-dig", - [ALC883_TARGA_DIG] = "targa-dig", - [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", - [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig", - [ALC883_ACER] = "acer", - [ALC883_ACER_ASPIRE] = "acer-aspire", - [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", - [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g", - [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g", - [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g", - [ALC883_MEDION] = "medion", - [ALC883_MEDION_WIM2160] = "medion-wim2160", - [ALC883_LAPTOP_EAPD] = "laptop-eapd", - [ALC883_LENOVO_101E_2ch] = "lenovo-101e", - [ALC883_LENOVO_NB0763] = "lenovo-nb0763", - [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", - [ALC888_LENOVO_SKY] = "lenovo-sky", - [ALC883_HAIER_W66] = "haier-w66", - [ALC888_3ST_HP] = "3stack-hp", - [ALC888_6ST_DELL] = "6stack-dell", - [ALC883_MITAC] = "mitac", - [ALC883_CLEVO_M540R] = "clevo-m540r", - [ALC883_CLEVO_M720] = "clevo-m720", - [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", - [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", - [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", - [ALC889A_INTEL] = "intel-alc889a", - [ALC889_INTEL] = "intel-x58", - [ALC1200_ASUS_P5Q] = "asus-p5q", - [ALC889A_MB31] = "mb31", - [ALC883_SONY_VAIO_TT] = "sony-vaio-tt", - [ALC882_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc882_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), - - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", - ALC888_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC888_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", - ALC888_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", - ALC888_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO), - SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO), - SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", - ALC888_ACER_ASPIRE_6530G), - SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", - ALC888_ACER_ASPIRE_6530G), - SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", - ALC888_ACER_ASPIRE_7730G), - /* default Acer -- disabled as it causes more problems. - * model=auto should work fine now - */ - /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */ - - SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), - - SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavilion", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP), - - SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), - SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), - SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), - SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), - SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), - SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), - - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), - SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), - SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), - SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), - SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), - - SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ - SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), - - SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), - SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), - SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD), - SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), - /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */ - SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), - SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx", - ALC883_FUJITSU_PI2515), - SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx", - ALC888_FUJITSU_XA3530), - SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), - SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY), - SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG), - SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), - - SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), - SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), - SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL), - SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL), - SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL), - SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG), - - {} -}; - -/* codec SSID table for Intel Mac */ -static const struct snd_pci_quirk alc882_ssid_cfg_tbl[] = { - SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO), - SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24), - SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31), - SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC885_MBA21), - SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31), - SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24), - SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91), - SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5), - SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC885_MB5), - /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2, - * so apparently no perfect solution yet - */ - SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5), - SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5), - SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3), - {} /* terminator */ -}; - -static const struct alc_config_preset alc882_presets[] = { - [ALC882_3ST_DIG] = { - .mixers = { alc882_base_mixer }, - .init_verbs = { alc882_base_init_verbs, - alc882_adc1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC882_6ST_DIG] = { - .mixers = { alc882_base_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, - alc882_adc1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), - .channel_mode = alc882_sixstack_modes, - .input_mux = &alc882_capture_source, - }, - [ALC882_ARIMA] = { - .mixers = { alc882_base_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), - .channel_mode = alc882_sixstack_modes, - .input_mux = &alc882_capture_source, - }, - [ALC882_W2JC] = { - .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_eapd_verbs, alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - }, - [ALC885_MBA21] = { - .mixers = { alc885_mba21_mixer }, - .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs }, - .num_dacs = 2, - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mba21_ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), - .input_mux = &alc882_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_mba21_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MBP3] = { - .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_mbp3_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = 2, - .dac_nids = alc882_dac_nids, - .hp_nid = 0x04, - .channel_mode = alc885_mbp_4ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes), - .input_mux = &alc882_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_mbp3_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MB5] = { - .mixers = { alc885_mb5_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_mb5_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mb5_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes), - .input_mux = &mb5_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_mb5_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MACMINI3] = { - .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_macmini3_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_macmini3_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes), - .input_mux = &macmini3_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_macmini3_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MACPRO] = { - .mixers = { alc882_macpro_mixer }, - .init_verbs = { alc882_macpro_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .input_mux = &alc882_capture_source, - .init_hook = alc885_macpro_init_hook, - }, - [ALC885_IMAC24] = { - .mixers = { alc885_imac24_mixer }, - .init_verbs = { alc885_imac24_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .input_mux = &alc882_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_imac24_setup, - .init_hook = alc885_imac24_init_hook, - }, - [ALC885_IMAC91] = { - .mixers = {alc885_imac91_mixer}, - .init_verbs = { alc885_imac91_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mba21_ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), - .input_mux = &alc889A_imac91_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_imac91_setup, - .init_hook = alc_hp_automute, - }, - [ALC882_TARGA] = { - .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc880_gpio3_init_verbs, alc882_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, - .capsrc_nids = alc882_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), - .channel_mode = alc882_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC882_ASUS_A7J] = { - .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_asus_a7j_verbs}, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, - .capsrc_nids = alc882_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), - .channel_mode = alc882_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC882_ASUS_A7M] = { - .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_eapd_verbs, alc880_gpio1_init_verbs, - alc882_asus_a7m_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC883_3ST_2ch_DIG] = { - .mixers = { alc883_3ST_2ch_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_3ST_6ch_DIG] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - }, - [ALC883_3ST_6ch] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - }, - [ALC883_3ST_6ch_INTEL] = { - .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc883_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes), - .channel_mode = alc883_3ST_6ch_intel_modes, - .need_dac_fix = 1, - .input_mux = &alc883_3stack_6ch_intel, - }, - [ALC889A_INTEL] = { - .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, - .init_verbs = { alc885_init_verbs, alc885_init_input_verbs, - alc_hp15_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), - .adc_nids = alc889_adc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc883_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), - .channel_mode = alc889_8ch_intel_modes, - .capsrc_nids = alc889_capsrc_nids, - .input_mux = &alc889_capture_source, - .setup = alc889_automute_setup, - .init_hook = alc_hp_automute, - .unsol_event = alc_sku_unsol_event, - .need_dac_fix = 1, - }, - [ALC889_INTEL] = { - .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, - .init_verbs = { alc885_init_verbs, alc889_init_input_verbs, - alc889_eapd_verbs, alc_hp15_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), - .adc_nids = alc889_adc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc883_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), - .channel_mode = alc889_8ch_intel_modes, - .capsrc_nids = alc889_capsrc_nids, - .input_mux = &alc889_capture_source, - .setup = alc889_automute_setup, - .init_hook = alc889_intel_init_hook, - .unsol_event = alc_sku_unsol_event, - .need_dac_fix = 1, - }, - [ALC883_6ST_DIG] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_TARGA_DIG] = { - .mixers = { alc883_targa_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, - alc883_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_targa_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC883_TARGA_2ch_DIG] = { - .mixers = { alc883_targa_2ch_mixer}, - .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, - alc883_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .adc_nids = alc883_adc_nids_alt, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), - .capsrc_nids = alc883_capsrc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_targa_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC883_TARGA_8ch_DIG] = { - .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, - alc883_targa_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes), - .channel_mode = alc883_4ST_8ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_targa_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC883_ACER] = { - .mixers = { alc883_base_mixer }, - /* On TravelMate laptops, GPIO 0 enables the internal speaker - * and the headphone jack. Turn this on and rely on the - * standard mute methods whenever the user wants to turn - * these outputs off. - */ - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_ACER_ASPIRE] = { - .mixers = { alc883_acer_aspire_mixer }, - .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_acer_aspire_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ACER_ASPIRE_4930G] = { - .mixers = { alc888_acer_aspire_4930g_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_4930g_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .const_channel_count = 6, - .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_2_capture_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_acer_aspire_4930g_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ACER_ASPIRE_6530G] = { - .mixers = { alc888_acer_aspire_6530_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_6530g_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_acer_aspire_6530_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_acer_aspire_6530g_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ACER_ASPIRE_8930G] = { - .mixers = { alc889_acer_aspire_8930g_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc889_acer_aspire_8930g_verbs, - alc889_eapd_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), - .adc_nids = alc889_adc_nids, - .capsrc_nids = alc889_capsrc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .const_channel_count = 6, - .num_mux_defs = - ARRAY_SIZE(alc889_capture_sources), - .input_mux = alc889_capture_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc889_acer_aspire_8930g_setup, - .init_hook = alc_hp_automute, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .power_hook = alc_power_eapd, -#endif - }, - [ALC888_ACER_ASPIRE_7730G] = { - .mixers = { alc883_3ST_6ch_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_7730G_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .const_channel_count = 6, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_acer_aspire_7730g_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_MEDION] = { - .mixers = { alc883_fivestack_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, - alc883_medion_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .adc_nids = alc883_adc_nids_alt, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), - .capsrc_nids = alc883_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_MEDION_WIM2160] = { - .mixers = { alc883_medion_wim2160_mixer }, - .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_medion_wim2160_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_LAPTOP_EAPD] = { - .mixers = { alc883_base_mixer }, - .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_CLEVO_M540R] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes), - .channel_mode = alc883_3ST_6ch_clevo_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - /* This machine has the hardware HP auto-muting, thus - * we need no software mute via unsol event - */ - }, - [ALC883_CLEVO_M720] = { - .mixers = { alc883_clevo_m720_mixer }, - .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_clevo_m720_unsol_event, - .setup = alc883_clevo_m720_setup, - .init_hook = alc883_clevo_m720_init_hook, - }, - [ALC883_LENOVO_101E_2ch] = { - .mixers = { alc883_lenovo_101e_2ch_mixer}, - .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .adc_nids = alc883_adc_nids_alt, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), - .capsrc_nids = alc883_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_lenovo_101e_capture_source, - .setup = alc883_lenovo_101e_setup, - .unsol_event = alc_sku_unsol_event, - .init_hook = alc_inithook, - }, - [ALC883_LENOVO_NB0763] = { - .mixers = { alc883_lenovo_nb0763_mixer }, - .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_lenovo_nb0763_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_lenovo_nb0763_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_LENOVO_MS7195_DIG] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_lenovo_ms7195_setup, - .init_hook = alc_inithook, - }, - [ALC883_HAIER_W66] = { - .mixers = { alc883_targa_2ch_mixer}, - .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_haier_w66_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_3ST_HP] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), - .channel_mode = alc888_3st_hp_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_3st_hp_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_6ST_DELL] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_6st_dell_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_MITAC] = { - .mixers = { alc883_mitac_mixer }, - .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_mitac_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_FUJITSU_PI2515] = { - .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, - .init_verbs = { alc883_init_verbs, - alc883_2ch_fujitsu_pi2515_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_fujitsu_pi2515_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_2ch_fujitsu_pi2515_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_FUJITSU_XA3530] = { - .mixers = { alc888_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, - alc888_fujitsu_xa3530_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes), - .channel_mode = alc888_4ST_8ch_intel_modes, - .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_2_capture_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_fujitsu_xa3530_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_LENOVO_SKY] = { - .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .need_dac_fix = 1, - .input_mux = &alc883_lenovo_sky_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_lenovo_sky_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ASUS_M90V] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_fujitsu_pi2515_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_mode2_setup, - .init_hook = alc_inithook, - }, - [ALC888_ASUS_EEE1601] = { - .mixers = { alc883_asus_eee1601_mixer }, - .cap_mixer = alc883_asus_eee1601_cap_mixer, - .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_asus_eee1601_capture_source, - .unsol_event = alc_sku_unsol_event, - .init_hook = alc883_eee1601_inithook, - }, - [ALC1200_ASUS_P5Q] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC1200_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc1200_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - }, - [ALC889A_MB31] = { - .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer}, - .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs, - alc880_gpio1_init_verbs }, - .adc_nids = alc883_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .capsrc_nids = alc883_capsrc_nids, - .dac_nids = alc883_dac_nids, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .channel_mode = alc889A_mb31_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes), - .input_mux = &alc889A_mb31_capture_source, - .dig_out_nid = ALC883_DIGOUT_NID, - .unsol_event = alc889A_mb31_unsol_event, - .init_hook = alc889A_mb31_automute, - }, - [ALC883_SONY_VAIO_TT] = { - .mixers = { alc883_vaiott_mixer }, - .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_vaiott_setup, - .init_hook = alc_hp_automute, - }, -}; - - -/* - * Pin config fixes - */ -enum { - PINFIX_ABIT_AW9D_MAX, - PINFIX_LENOVO_Y530, - PINFIX_PB_M5210, - PINFIX_ACER_ASPIRE_7736, -}; - -static const struct alc_fixup alc882_fixups[] = { - [PINFIX_ABIT_AW9D_MAX] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } - } - }, - [PINFIX_LENOVO_Y530] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x15, 0x99130112 }, /* rear int speakers */ - { 0x16, 0x99130111 }, /* subwoofer */ - { } - } - }, - [PINFIX_PB_M5210] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, - {} - } - }, - [PINFIX_ACER_ASPIRE_7736] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, - }, -}; - -static const struct snd_pci_quirk alc882_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), - SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), - SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), - {} -}; - -/* - * BIOS auto configuration - */ -static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - - nid = spec->adc_nids[adc_idx]; - /* mute ADC */ - if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - return; - } - if (!spec->capsrc_nids) - return; - nid = spec->capsrc_nids[adc_idx]; - if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); -} - -static void alc_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int c, nums; - - for (c = 0; c < spec->num_adc_nids; c++) - alc_auto_init_adc(codec, c); - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - for (c = 0; c < nums; c++) - alc_mux_select(codec, 0, spec->cur_mux[c], true); -} - -/* add mic boosts if needed */ -static int alc_auto_add_mic_boost(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - int type_idx = 0; - hda_nid_t nid; - const char *prev_label = NULL; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type > AUTO_PIN_MIC) - break; - nid = cfg->inputs[i].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - const char *label; - char boost_label[32]; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", label); - err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* almost identical with ALC880 parser... */ -static int alc882_parse_auto_config(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc882_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - return 1; /* config found */ -} - -/* additional initialization for auto-configuration model */ -static void alc882_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -static int patch_alc882(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - - switch (codec->vendor_id) { - case 0x10ec0882: - case 0x10ec0885: - break; - default: - /* ALC883 and variants */ - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - break; - } - - board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST, - alc882_models, - alc882_cfg_tbl); - - if (board_config < 0 || board_config >= ALC882_MODEL_LAST) - board_config = snd_hda_check_board_codec_sid_config(codec, - ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl); - - if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC882_AUTO; - } - - if (board_config == ALC882_AUTO) { - alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - alc_auto_parse_customize_define(codec); - - if (board_config == ALC882_AUTO) { - /* automatic parse from the BIOS config */ - err = alc882_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 = ALC882_3ST_DIG; - } - } - - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - - if (board_config != ALC882_AUTO) - setup_preset(codec, &alc882_presets[board_config]); - - if (!spec->adc_nids && spec->input_mux) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - set_capture_mixer(codec); - - if (has_cdefine_beep(codec)) - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - spec->vmaster_nid = 0x0c; - - codec->patch_ops = alc_patch_ops; - if (board_config == ALC882_AUTO) - spec->init_hook = alc882_auto_init; - - alc_init_jacks(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc882_loopbacks; -#endif - - return 0; -} - - -/* - * ALC262 support - */ - -#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID -#define ALC262_DIGIN_NID ALC880_DIGIN_NID - -#define alc262_dac_nids alc260_dac_nids -#define alc262_adc_nids alc882_adc_nids -#define alc262_adc_nids_alt alc882_adc_nids_alt -#define alc262_capsrc_nids alc882_capsrc_nids -#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt - -#define alc262_modes alc260_modes -#define alc262_capture_source alc882_capture_source - -static const hda_nid_t alc262_dmic_adc_nids[1] = { - /* ADC0 */ - 0x09 -}; - -static const hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 }; - -static const struct snd_kcontrol_new alc262_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* update HP, line and mono-out pins according to the master switch */ -#define alc262_hp_master_update alc260_hp_master_update - -static void alc262_hp_bpc_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static void alc262_hp_wildwest_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -#define alc262_hp_master_sw_get alc260_hp_master_sw_get -#define alc262_hp_master_sw_put alc260_hp_master_sw_put - -#define ALC262_HP_MASTER_SWITCH \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Master Playback Switch", \ - .info = snd_ctl_boolean_mono_info, \ - .get = alc262_hp_master_sw_get, \ - .put = alc262_hp_master_sw_put, \ - }, \ - { \ - .iface = NID_MAPPING, \ - .name = "Master Playback Switch", \ - .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \ - } - - -static const struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { - ALC262_HP_MASTER_SWITCH, - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, - HDA_OUTPUT), - 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { - ALC262_HP_MASTER_SWITCH, - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x1a, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { - HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Rear Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hp_t5735_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - 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 Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_hp_t5735_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -static const struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_hp_rp5700_verbs[] = { - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, - {} -}; - -static const struct hda_input_mux alc262_hp_rp5700_capture_source = { - .num_items = 1, - .items = { - { "Line", 0x1 }, - }, -}; - -/* bind hp and internal speaker mute (with plug check) as master switch */ -#define alc262_hippo_master_update alc262_hp_master_update -#define alc262_hippo_master_sw_get alc262_hp_master_sw_get -#define alc262_hippo_master_sw_put alc262_hp_master_sw_put - -#define ALC262_HIPPO_MASTER_SWITCH \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Master Playback Switch", \ - .info = snd_ctl_boolean_mono_info, \ - .get = alc262_hippo_master_sw_get, \ - .put = alc262_hippo_master_sw_put, \ - }, \ - { \ - .iface = NID_MAPPING, \ - .name = "Master Playback Switch", \ - .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \ - (SUBDEV_SPEAKER(0) << 16), \ - } - -static const struct snd_kcontrol_new alc262_hippo_mixer[] = { - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_hippo1_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hippo_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc262_hippo1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - - -static const struct snd_kcontrol_new alc262_sony_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_benq_t31_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_tyan_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT), - 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_tyan_verbs[] = { - /* Headphone automute */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* P11 AUX_IN, white 4-pin connector */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1}, - {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93}, - {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19}, - - {} -}; - -/* unsolicited event for HP jack sensing */ -static void alc262_tyan_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - - -#define alc262_capture_mixer alc882_capture_mixer -#define alc262_capture_alt_mixer alc882_capture_alt_mixer - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc262_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (CD, 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 */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, 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, 0x40}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - - { } -}; - -static const struct hda_verb alc262_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc262_hippo1_unsol_verbs[] = { - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc262_sony_unsol_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_toshiba_s06_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x09}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static void alc262_toshiba_s06_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -/* - * nec model - * 0x15 = headphone - * 0x16 = internal speaker - * 0x18 = external mic - */ - -static const struct snd_kcontrol_new alc262_nec_mixer[] = { - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT), - - 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 Volume", 0x18, 0, HDA_INPUT), - - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_nec_verbs[] = { - /* Unmute Speaker */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Headphone */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* External mic to headphone */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* External mic to speaker */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {} -}; - -/* - * fujitsu model - * 0x14 = headphone/spdif-out, 0x15 = internal speaker, - * 0x1b = port replicator headphone out - */ - -#define ALC_HP_EVENT ALC880_HP_EVENT - -static const struct hda_verb alc262_fujitsu_unsol_verbs[] = { - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc262_lenovo_3000_init_verbs[] = { - /* Front Mic pin: input vref at 50% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {} -}; - -static const struct hda_input_mux alc262_fujitsu_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc262_HP_capture_source = { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "AUX IN", 0x6 }, - }, -}; - -static const struct hda_input_mux alc262_HP_D7000_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x2 }, - { "Line", 0x1 }, - { "CD", 0x4 }, - }, -}; - -static void alc262_fujitsu_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.hp_pins[1] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* bind volumes of both NID 0x0c and 0x0d */ -static const struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc262_fujitsu_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, - .info = snd_ctl_boolean_mono_info, - .get = alc262_hp_master_sw_get, - .put = alc262_hp_master_sw_put, - }, - { - .iface = NID_MAPPING, - .name = "Master Playback Switch", - .private_value = 0x1b, - }, - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static void alc262_lenovo_3000_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = snd_ctl_boolean_mono_info, - .get = alc262_hp_master_sw_get, - .put = alc262_hp_master_sw_put, - }, - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - ALC262_HIPPO_MASTER_SWITCH, - 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 Volume", 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 Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -/* additional init verbs for Benq laptops */ -static const struct hda_verb alc262_EAPD_verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, - {} -}; - -static const struct hda_verb alc262_benq_t31_EAPD_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, - {} -}; - -/* Samsung Q1 Ultra Vista model setup */ -static const struct snd_kcontrol_new alc262_ultra_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Mic Boost Volume", 0x15, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_ultra_verbs[] = { - /* output mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* speaker */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - /* internal mic */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* ADC, choose mic */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, - {} -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_ultra_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int mute; - - mute = 0; - /* auto-mute only when HP is used as HP */ - if (!spec->cur_mux[0]) { - spec->jack_present = snd_hda_jack_detect(codec, 0x15); - if (spec->jack_present) - mute = HDA_AMP_MUTE; - } - /* mute/unmute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - /* mute/unmute HP */ - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); -} - -/* unsolicited event for HP jack sensing */ -static void alc262_ultra_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc262_ultra_automute(codec); -} - -static const struct hda_input_mux alc262_ultra_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Headphone", 0x7 }, - }, -}; - -static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int ret; - - ret = alc_mux_enum_put(kcontrol, ucontrol); - if (!ret) - return 0; - /* reprogram the HP pin as mic or HP according to the input source */ - snd_hda_codec_write_cache(codec, 0x15, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); - alc262_ultra_automute(codec); /* mute/unmute HP */ - return ret; -} - -static const struct snd_kcontrol_new alc262_ultra_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, - .name = "Capture Source", - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc262_ultra_mux_enum_put, - }, - { - .iface = NID_MAPPING, - .name = "Capture Source", - .private_value = 0x15, - }, - { } /* end */ -}; - -/* We use two mixers depending on the output pin; 0x16 is a mono output - * and thus it's bound with a different mixer. - * This function returns which mixer amp should be used. - */ -static int alc262_check_volbit(hda_nid_t nid) -{ - if (!nid) - return 0; - else if (nid == 0x16) - return 2; - else - return 1; -} - -static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vbits, int idx) -{ - unsigned long val; - int vbit; - - vbit = alc262_check_volbit(nid); - if (!vbit) - return 0; - if (*vbits & vbit) /* a volume control for this mixer already there */ - return 0; - *vbits |= vbit; - if (vbit == 2) - val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); - return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val); -} - -static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int idx) -{ - unsigned long val; - - if (!nid) - return 0; - if (nid == 0x16) - val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val); -} - -/* add playback controls from the parsed DAC table */ -static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - const char *pfx; - int vbits; - int i, index, err; - - spec->multiout.num_dacs = 1; /* only use one dac */ - spec->multiout.dac_nids = spec->private_dac_nids; - spec->private_dac_nids[0] = 2; - - for (i = 0; i < 2; i++) { - pfx = alc_get_line_out_pfx(spec, i, true, &index); - if (!pfx) - pfx = "PCM"; - err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, - index); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i], - "Speaker", i); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i], - "Headphone", i); - if (err < 0) - return err; - } - } - - vbits = alc262_check_volbit(cfg->line_out_pins[0]) | - alc262_check_volbit(cfg->speaker_pins[0]) | - alc262_check_volbit(cfg->hp_pins[0]); - vbits = 0; - for (i = 0; i < 2; i++) { - pfx = alc_get_line_out_pfx(spec, i, true, &index); - if (!pfx) - pfx = "PCM"; - err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx, - &vbits, i); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i], - "Speaker", &vbits, i); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i], - "Headphone", &vbits, i); - if (err < 0) - return err; - } - } - return 0; -} - -static const struct hda_verb alc262_HP_BPC_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (CD, 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)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - - /* - * Set up output mixers (0x0c - 0x0e) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, 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)}, - - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */ - /* Input mixer1: only unmute Mic */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, - - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } -}; - -static const struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (CD, 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)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* - * Set up output mixers (0x0c - 0x0e) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, 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)}, - - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */ - - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - - /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/ - /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } -}; - -static const struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -/* - * Pin config fixes - */ -enum { - PINFIX_FSC_H270, - PINFIX_HP_Z200, -}; - -static const struct alc_fixup alc262_fixups[] = { - [PINFIX_FSC_H270] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0221142f }, /* front HP */ - { 0x1b, 0x0121141f }, /* rear HP */ - { } - } - }, - [PINFIX_HP_Z200] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x16, 0x99130120 }, /* internal speaker */ - { } - } - }, -}; - -static const struct snd_pci_quirk alc262_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", PINFIX_HP_Z200), - SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270), - {} -}; - - -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc262_loopbacks alc880_loopbacks -#endif - -/* - * BIOS auto configuration - */ -static int alc262_parse_auto_config(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int err; - static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc262_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 = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - return 1; -} - - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc262_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -/* - * configuration and preset - */ -static const char * const alc262_models[ALC262_MODEL_LAST] = { - [ALC262_BASIC] = "basic", - [ALC262_HIPPO] = "hippo", - [ALC262_HIPPO_1] = "hippo_1", - [ALC262_FUJITSU] = "fujitsu", - [ALC262_HP_BPC] = "hp-bpc", - [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", - [ALC262_HP_TC_T5735] = "hp-tc-t5735", - [ALC262_HP_RP5700] = "hp-rp5700", - [ALC262_BENQ_ED8] = "benq", - [ALC262_BENQ_T31] = "benq-t31", - [ALC262_SONY_ASSAMD] = "sony-assamd", - [ALC262_TOSHIBA_S06] = "toshiba-s06", - [ALC262_TOSHIBA_RX1] = "toshiba-rx1", - [ALC262_ULTRA] = "ultra", - [ALC262_LENOVO_3000] = "lenovo-3000", - [ALC262_NEC] = "nec", - [ALC262_TYAN] = "tyan", - [ALC262_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc262_cfg_tbl[] = { - SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), - SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series", - ALC262_HP_BPC), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series", - ALC262_HP_BPC), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series", - ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", - ALC262_AUTO), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series", - ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", - ALC262_HP_TC_T5735), - SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), - SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), - SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */ - SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06), - SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO), -#if 0 /* disable the quirk since model=auto works better in recent versions */ - SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", - ALC262_SONY_ASSAMD), -#endif - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", - ALC262_TOSHIBA_RX1), - SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), - SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), - SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN), - SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1", - ALC262_ULTRA), - SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO), - SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), - SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), - SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), - SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), - {} -}; - -static const struct alc_config_preset alc262_presets[] = { - [ALC262_BASIC] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_HIPPO] = { - .mixers = { alc262_hippo_mixer }, - .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_HIPPO_1] = { - .mixers = { alc262_hippo1_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo1_setup, - .init_hook = alc_inithook, - }, - [ALC262_FUJITSU] = { - .mixers = { alc262_fujitsu_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, - alc262_fujitsu_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_fujitsu_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_fujitsu_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_BPC] = { - .mixers = { alc262_HP_BPC_mixer }, - .init_verbs = { alc262_HP_BPC_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_bpc_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_BPC_D7000_WF] = { - .mixers = { alc262_HP_BPC_WildWest_mixer }, - .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_D7000_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_wildwest_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_BPC_D7000_WL] = { - .mixers = { alc262_HP_BPC_WildWest_mixer, - alc262_HP_BPC_WildWest_option_mixer }, - .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_D7000_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_wildwest_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_TC_T5735] = { - .mixers = { alc262_hp_t5735_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_t5735_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_RP5700] = { - .mixers = { alc262_hp_rp5700_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_hp_rp5700_capture_source, - }, - [ALC262_BENQ_ED8] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_SONY_ASSAMD] = { - .mixers = { alc262_sony_mixer }, - .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_BENQ_T31] = { - .mixers = { alc262_benq_t31_mixer }, - .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, - alc_hp15_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_ULTRA] = { - .mixers = { alc262_ultra_mixer }, - .cap_mixer = alc262_ultra_capture_mixer, - .init_verbs = { alc262_ultra_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_ultra_capture_source, - .adc_nids = alc262_adc_nids, /* ADC0 */ - .capsrc_nids = alc262_capsrc_nids, - .num_adc_nids = 1, /* single ADC */ - .unsol_event = alc262_ultra_unsol_event, - .init_hook = alc262_ultra_automute, - }, - [ALC262_LENOVO_3000] = { - .mixers = { alc262_lenovo_3000_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, - alc262_lenovo_3000_unsol_verbs, - alc262_lenovo_3000_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_fujitsu_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_lenovo_3000_setup, - .init_hook = alc_inithook, - }, - [ALC262_NEC] = { - .mixers = { alc262_nec_mixer }, - .init_verbs = { alc262_nec_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_TOSHIBA_S06] = { - .mixers = { alc262_toshiba_s06_mixer }, - .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs, - alc262_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .capsrc_nids = alc262_dmic_capsrc_nids, - .dac_nids = alc262_dac_nids, - .adc_nids = alc262_dmic_adc_nids, /* ADC0 */ - .num_adc_nids = 1, /* single ADC */ - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_toshiba_s06_setup, - .init_hook = alc_inithook, - }, - [ALC262_TOSHIBA_RX1] = { - .mixers = { alc262_toshiba_rx1_mixer }, - .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_TYAN] = { - .mixers = { alc262_tyan_mixer }, - .init_verbs = { alc262_init_verbs, alc262_tyan_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_tyan_setup, - .init_hook = alc_hp_automute, - }, -}; - -static int patch_alc262(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; - - spec->mixer_nid = 0x0b; - -#if 0 - /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is - * under-run - */ - { - int tmp; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); - } -#endif - alc_auto_parse_customize_define(codec); - - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - - board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, - alc262_models, - alc262_cfg_tbl); - - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC262_AUTO; - } - - if (board_config == ALC262_AUTO) { - alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - if (board_config == ALC262_AUTO) { - /* automatic parse from the BIOS config */ - err = alc262_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 = ALC262_BASIC; - } - } - - if (!spec->no_analog && has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - - if (board_config != ALC262_AUTO) - setup_preset(codec, &alc262_presets[board_config]); - - if (!spec->adc_nids && spec->input_mux) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - - spec->vmaster_nid = 0x0c; - - codec->patch_ops = alc_patch_ops; - if (board_config == ALC262_AUTO) - spec->init_hook = alc262_auto_init; - spec->shutup = alc_eapd_shutup; - - alc_init_jacks(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc262_loopbacks; -#endif - - return 0; -} - -/* - * ALC268 channel source setting (2 channel) - */ -#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID -#define alc268_modes alc260_modes - -static const hda_nid_t alc268_dac_nids[2] = { - /* front, hp */ - 0x02, 0x03 -}; - -static const hda_nid_t alc268_adc_nids[2] = { - /* ADC0-1 */ - 0x08, 0x07 -}; - -static const hda_nid_t alc268_adc_nids_alt[1] = { - /* ADC0 */ - 0x08 -}; - -static const hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; - -static const struct snd_kcontrol_new alc268_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", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc268_toshiba_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -/* bind Beep switches of both NID 0x0f and 0x10 */ -static const struct hda_bind_ctls alc268_bind_beep_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc268_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), - HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), - { } -}; - -static const struct hda_verb alc268_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* Toshiba specific */ -static const struct hda_verb alc268_toshiba_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -/* Acer specific */ -/* bind volumes of both NID 0x02 and 0x03 */ -static const struct hda_bind_ctls alc268_acer_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static void alc268_acer_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -#define alc268_acer_master_sw_get alc262_hp_master_sw_get -#define alc268_acer_master_sw_put alc262_hp_master_sw_put - -static const struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x15, - .info = snd_ctl_boolean_mono_info, - .get = alc268_acer_master_sw_get, - .put = alc268_acer_master_sw_put, - }, - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc268_acer_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, - .info = snd_ctl_boolean_mono_info, - .get = alc268_acer_master_sw_get, - .put = alc268_acer_master_sw_put, - }, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, - .info = snd_ctl_boolean_mono_info, - .get = alc268_acer_master_sw_get, - .put = alc268_acer_master_sw_put, - }, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -static const struct hda_verb alc268_acer_aspire_one_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x06}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017}, - { } -}; - -static const struct hda_verb alc268_acer_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -/* unsolicited event for HP jack sensing */ -#define alc268_toshiba_setup alc262_hippo_setup - -static void alc268_acer_lc_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; -} - -static const struct snd_kcontrol_new alc268_dell_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct hda_verb alc268_dell_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - { } -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc268_dell_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct hda_verb alc267_quanta_il1_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - { } -}; - -static void alc267_quanta_il1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc268_base_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}, - - /* - * Set up output mixers (0x0c - 0x0e) - */ - /* set vol=0 to output mixers */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1d, 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}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - /* set PCBEEP vol = 0, mute connections */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* Unmute Selector 23h,24h and set the default input to mic-in */ - - {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - { } -}; - -/* only for model=test */ -#ifdef CONFIG_SND_DEBUG -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc268_volume_init_verbs[] = { - /* set output DAC */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - { } -}; -#endif /* CONFIG_SND_DEBUG */ - -/* set PCBEEP vol = 0, mute connections */ -static const struct hda_verb alc268_beep_init_verbs[] = { - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; - -static const struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc268_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), - _DEFINE_CAPSRC(1), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc268_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT), - _DEFINE_CAPSRC(2), - { } /* end */ -}; - -static const struct hda_input_mux alc268_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x3 }, - }, -}; - -static const struct hda_input_mux alc268_acer_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux alc268_acer_dmic_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x6 }, - { "Line", 0x2 }, - }, -}; - -#ifdef CONFIG_SND_DEBUG -static const struct snd_kcontrol_new alc268_test_mixer[] = { - /* Volume widgets */ - HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), - HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), - HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), - HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), - HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), - /* The below appears problematic on some hardwares */ - /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ - HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), - - /* Modes for retasking pin widgets */ - ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), - - /* Controls for GPIO pins, assuming they are configured as outputs */ - ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), - ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), - ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), - ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), - - /* Switches to allow the digital SPDIF output pin to be enabled. - * The ALC268 does not have an SPDIF input. - */ - ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01), - - /* A switch allowing EAPD to be enabled. Some laptops seem to use - * this output to turn on an external amplifier. - */ - ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), - ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), - - { } /* end */ -}; -#endif - -/* create input playback/capture controls for the given pin */ -static int alc268_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: - case 0x16: - dac = 0x02; - break; - case 0x15: - case 0x1a: /* ALC259/269 only */ - case 0x1b: /* ALC259/269 only */ - case 0x21: /* ALC269vb has this pin, too */ - dac = 0x03; - break; - default: - snd_printd(KERN_WARNING "hda_codec: " - "ignoring pin 0x%x as unknown\n", nid); - 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; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - - if (nid != 0x16) - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); - else /* mono */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); - if (err < 0) - return err; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc268_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; - int index; - name = alc_get_line_out_pfx(spec, 0, true, &index); - err = alc268_new_analog_output(spec, nid, name, 0); - if (err < 0) - return err; - } - - nid = cfg->speaker_pins[0]; - if (nid == 0x1d) { - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } else if (nid) { - err = alc268_new_analog_output(spec, nid, "Speaker", 0); - if (err < 0) - return err; - } - nid = cfg->hp_pins[0]; - if (nid) { - err = alc268_new_analog_output(spec, nid, "Headphone", 0); - if (err < 0) - return err; - } - - nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; - if (nid == 0x16) { - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type) -{ - int idx; - - alc_set_pin_output(codec, nid, pin_type); - if (snd_hda_get_conn_list(codec, nid, NULL) <= 1) - return; - if (nid == 0x14 || nid == 0x16) - idx = 0; - else - idx = 1; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) -{ - if (!nid) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); -} - -static void alc268_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc268_auto_set_output_and_unmute(codec, nid, pin_type); - } - /* mute DACs */ - for (i = 0; i < spec->multiout.num_dacs; i++) - alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]); -} - -static void alc268_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - int i; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - pin = spec->autocfg.hp_pins[i]; - alc268_auto_set_output_and_unmute(codec, pin, PIN_HP); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - pin = spec->autocfg.speaker_pins[i]; - alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT); - } - if (spec->autocfg.mono_out_pin) - snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* mute DACs */ - alc268_auto_init_dac(codec, spec->multiout.hp_nid); - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) - alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); -} - -static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; - hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; - hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; - unsigned int dac_vol1, dac_vol2; - - if (line_nid == 0x1d || speaker_nid == 0x1d) { - snd_hda_codec_write(codec, speaker_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* mute mixer inputs from 0x1d */ - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } else { - /* unmute mixer inputs from 0x1d */ - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); - } - - dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */ - if (line_nid == 0x14) - dac_vol2 = AMP_OUT_ZERO; - else if (line_nid == 0x15) - dac_vol1 = AMP_OUT_ZERO; - if (hp_nid == 0x14) - dac_vol2 = AMP_OUT_ZERO; - else if (hp_nid == 0x15) - dac_vol1 = AMP_OUT_ZERO; - if (line_nid != 0x16 || hp_nid != 0x16 || - spec->autocfg.line_out_pins[1] != 0x16 || - spec->autocfg.line_out_pins[2] != 0x16) - dac_vol1 = dac_vol2 = AMP_OUT_ZERO; - - snd_hda_codec_write(codec, 0x02, 0, - AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1); - snd_hda_codec_write(codec, 0x03, 0, - AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); + if (num_pins < 2) + return 0; + return num_pins; } -/* - * BIOS auto configuration - */ -static int alc268_parse_auto_config(struct hda_codec *codec) +static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int err; - static const 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) { - 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 = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->multi_ios + 1; + if (uinfo->value.enumerated.item > spec->multi_ios) + uinfo->value.enumerated.item = spec->multi_ios; + sprintf(uinfo->value.enumerated.name, "%dch", + (uinfo->value.enumerated.item + 1) * 2); + return 0; +} - spec->multiout.max_channels = 2; +static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + return 0; +} - dig_only: - /* digital only support output */ - alc_auto_parse_digital(codec); - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); +static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = spec->multi_io[idx].pin; - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { - add_mixer(spec, alc268_beep_mixer); - add_verb(spec, alc268_beep_init_verbs); + if (!spec->multi_io[idx].ctl_in) + spec->multi_io[idx].ctl_in = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (output) { + snd_hda_codec_update_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); + alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); + } else { + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_update_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->multi_io[idx].ctl_in); } - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - return 1; + return 0; } -/* init callback for auto-configuration model -- overriding the default init */ -static void alc268_auto_init(struct hda_codec *codec) +static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - alc268_auto_init_multi_out(codec); - alc268_auto_init_hp_out(codec); - alc268_auto_init_mono_speaker_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -/* - * configuration and preset - */ -static const char * const alc268_models[ALC268_MODEL_LAST] = { - [ALC267_QUANTA_IL1] = "quanta-il1", - [ALC268_3ST] = "3stack", - [ALC268_TOSHIBA] = "toshiba", - [ALC268_ACER] = "acer", - [ALC268_ACER_DMIC] = "acer-dmic", - [ALC268_ACER_ASPIRE_ONE] = "acer-aspire", - [ALC268_DELL] = "dell", - [ALC268_ZEPTO] = "zepto", -#ifdef CONFIG_SND_DEBUG - [ALC268_TEST] = "test", -#endif - [ALC268_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc268_cfg_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), - 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(0x1025, 0x015b, "Acer Aspire One", - ALC268_ACER_ASPIRE_ONE), - SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), - SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron 910", ALC268_AUTO), - SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0, - "Dell Inspiron Mini9/Vostro A90", ALC268_DELL), - /* almost compatible with toshiba but with optional digital outs; - * auto-probing seems working fine - */ - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series", - ALC268_AUTO), - SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), - SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), - SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), - {} -}; + int i, ch; -/* Toshiba laptops have no unique PCI SSID but only codec SSID */ -static const struct snd_pci_quirk alc268_ssid_cfg_tbl[] = { - SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO), - SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO), - SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05", - ALC268_TOSHIBA), - {} -}; + ch = ucontrol->value.enumerated.item[0]; + if (ch < 0 || ch > spec->multi_ios) + return -EINVAL; + if (ch == (spec->ext_channel_count - 1) / 2) + return 0; + spec->ext_channel_count = (ch + 1) * 2; + for (i = 0; i < spec->multi_ios; i++) + alc_set_multi_io(codec, i, i < ch); + spec->multiout.max_channels = spec->ext_channel_count; + return 1; +} -static const struct alc_config_preset alc268_presets[] = { - [ALC267_QUANTA_IL1] = { - .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer, - alc268_capture_nosrc_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc267_quanta_il1_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, - .unsol_event = alc_sku_unsol_event, - .setup = alc267_quanta_il1_setup, - .init_hook = alc_inithook, - }, - [ALC268_3ST] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, - alc268_beep_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, - .capsrc_nids = alc268_capsrc_nids, - .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_toshiba_mixer, alc268_capture_alt_mixer, - alc268_beep_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, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_toshiba_setup, - .init_hook = alc_inithook, - }, - [ALC268_ACER] = { - .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, - alc268_beep_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, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_acer_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_acer_setup, - .init_hook = alc_inithook, - }, - [ALC268_ACER_DMIC] = { - .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer, - alc268_beep_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, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_acer_dmic_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_acer_setup, - .init_hook = alc_inithook, - }, - [ALC268_ACER_ASPIRE_ONE] = { - .mixers = { alc268_acer_aspire_one_mixer, - alc268_beep_mixer, - alc268_capture_nosrc_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_acer_aspire_one_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, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_acer_lc_setup, - .init_hook = alc_inithook, - }, - [ALC268_DELL] = { - .mixers = { alc268_dell_mixer, alc268_beep_mixer, - alc268_capture_nosrc_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, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_dell_setup, - .init_hook = alc_inithook, - }, - [ALC268_ZEPTO] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, - alc268_beep_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, - .capsrc_nids = alc268_capsrc_nids, - .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, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_toshiba_setup, - .init_hook = alc_inithook, - }, -#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, - alc268_beep_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, - .capsrc_nids = alc268_capsrc_nids, - .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 const struct snd_kcontrol_new alc_auto_channel_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_auto_ch_mode_info, + .get = alc_auto_ch_mode_get, + .put = alc_auto_ch_mode_put, }; -static int patch_alc268(struct hda_codec *codec) +static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, + int (*fill_dac)(struct hda_codec *)) { - struct alc_spec *spec; - int board_config; - int i, has_beep, err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int location, defcfg; + int num_pins; - codec->spec = spec; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) { + /* use HP as primary out */ + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + if (fill_dac) + fill_dac(codec); + } + if (cfg->line_outs != 1 || + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + return 0; - /* ALC268 has no aa-loopback mixer */ + defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]); + location = get_defcfg_location(defcfg); - board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST, - alc268_models, - alc268_cfg_tbl); + num_pins = alc_auto_fill_multi_ios(codec, location); + if (num_pins > 0) { + struct snd_kcontrol_new *knew; - if (board_config < 0 || board_config >= ALC268_MODEL_LAST) - board_config = snd_hda_check_board_codec_sid_config(codec, - ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl); + knew = alc_kcontrol_new(spec); + if (!knew) + return -ENOMEM; + *knew = alc_auto_channel_mode_enum; + knew->name = kstrdup("Channel Mode", GFP_KERNEL); + if (!knew->name) + return -ENOMEM; - if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC268_AUTO; + spec->multi_ios = num_pins; + spec->ext_channel_count = 2; + spec->multiout.num_dacs = num_pins + 1; } + return 0; +} - 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; - } - } +/* filter out invalid adc_nids (and capsrc_nids) that don't give all + * active input pins + */ +static void alc_remove_invalid_adc_nids(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + int i, n, nums; - if (board_config != ALC268_AUTO) - setup_preset(codec, &alc268_presets[board_config]); + imux = spec->input_mux; + if (!imux) + return; + if (spec->dyn_adc_switch) + return; - has_beep = 0; - for (i = 0; i < spec->num_mixers; i++) { - if (spec->mixers[i] == alc268_beep_mixer) { - has_beep = 1; - break; + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + hda_nid_t cap = spec->private_capsrc_nids[n]; + int num_conns = snd_hda_get_conn_list(codec, cap, NULL); + for (i = 0; i < imux->num_items; i++) { + hda_nid_t pin = spec->imux_pins[i]; + if (pin) { + if (get_connection_index(codec, cap, pin) < 0) + break; + } else if (num_conns <= imux->items[i].index) + break; + } + if (i >= imux->num_items) { + adc_nids[nums] = spec->private_adc_nids[n]; + capsrc_nids[nums++] = cap; } } - - if (has_beep) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; + if (!nums) { + /* check whether ADC-switch is possible */ + if (!alc_check_dyn_adc_switch(codec)) { + printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" + " using fallback 0x%x\n", + codec->chip_name, spec->private_adc_nids[0]); + spec->num_adc_nids = 1; + spec->auto_mic = 0; + return; } - if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) - /* override the amp caps for beep generator */ - snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, - (0x0c << AC_AMPCAP_OFFSET_SHIFT) | - (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); + } else if (nums != spec->num_adc_nids) { + memcpy(spec->private_adc_nids, adc_nids, + nums * sizeof(hda_nid_t)); + memcpy(spec->private_capsrc_nids, capsrc_nids, + nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; } - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); + if (spec->auto_mic) + alc_auto_mic_check_imux(codec); /* check auto-mic setups */ + else if (spec->input_mux->num_items == 1) + spec->num_adc_nids = 1; /* reduce to a single ADC */ +} + +/* + * initialize ADC paths + */ +static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + + nid = spec->adc_nids[adc_idx]; + /* mute ADC */ + if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + return; } + if (!spec->capsrc_nids) + return; + nid = spec->capsrc_nids[adc_idx]; + if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +} - if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(codec); +static void alc_auto_init_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int c, nums; - spec->vmaster_nid = 0x02; + for (c = 0; c < spec->num_adc_nids; c++) + alc_auto_init_adc(codec, c); + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + for (c = 0; c < nums; c++) + alc_mux_select(codec, 0, spec->cur_mux[c], true); +} - codec->patch_ops = alc_patch_ops; - if (board_config == ALC268_AUTO) - spec->init_hook = alc268_auto_init; - spec->shutup = alc_eapd_shutup; +/* add mic boosts if needed */ +static int alc_auto_add_mic_boost(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + int type_idx = 0; + hda_nid_t nid; + const char *prev_label = NULL; - alc_init_jacks(codec); + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type > AUTO_PIN_MIC) + break; + nid = cfg->inputs[i].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + const char *label; + char boost_label[32]; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + snprintf(boost_label, sizeof(boost_label), + "%s Boost Volume", label); + err = add_control(spec, ALC_CTL_WIDGET_VOL, + boost_label, type_idx, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + } return 0; } -/* - * ALC269 channel source setting (2 channel) - */ -#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID +/* select or unmute the given capsrc route */ +static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, + int idx) +{ + if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { + snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, + HDA_AMP_MUTE, 0); + } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) { + snd_hda_codec_write_cache(codec, cap, 0, + AC_VERB_SET_CONNECT_SEL, idx); + } +} -#define alc269_dac_nids alc260_dac_nids +/* 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; + int i; -static const hda_nid_t alc269_adc_nids[1] = { - /* ADC1 */ - 0x08, -}; + if (!pin) + return 0; + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[i] : spec->adc_nids[i]; + int idx; -static const hda_nid_t alc269_capsrc_nids[1] = { - 0x23, -}; + idx = get_connection_index(codec, cap, pin); + if (idx < 0) + continue; + select_or_unmute_capsrc(codec, cap, idx); + return i; /* return the found index */ + } + return -1; /* not found */ +} -static const hda_nid_t alc269vb_adc_nids[1] = { - /* ADC1 */ - 0x09, -}; +/* initialize some special cases for input sources */ +static void alc_init_special_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; -static const hda_nid_t alc269vb_capsrc_nids[1] = { - 0x22, -}; + for (i = 0; i < spec->autocfg.num_inputs; i++) + init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); +} -#define alc269_modes alc260_modes -#define alc269_capture_source alc880_lg_lw_capture_source - -static const 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 Volume", 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 Volume", 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 */ -}; +/* assign appropriate capture mixers */ +static void set_capture_mixer(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + static const struct snd_kcontrol_new *caps[2][3] = { + { alc_capture_mixer_nosrc1, + alc_capture_mixer_nosrc2, + alc_capture_mixer_nosrc3 }, + { alc_capture_mixer1, + alc_capture_mixer2, + alc_capture_mixer3 }, + }; -static const struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_acer_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - }, - 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 Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; + /* check whether either of ADC or MUX has a volume control */ + if (!(query_amp_caps(codec, spec->adc_nids[0], HDA_INPUT) & + AC_AMPCAP_NUM_STEPS)) { + if (!spec->capsrc_nids) + return; /* no volume */ + if (!(query_amp_caps(codec, spec->capsrc_nids[0], HDA_OUTPUT) & + AC_AMPCAP_NUM_STEPS)) + return; /* no volume in capsrc, too */ + spec->vol_in_capsrc = 1; + } -static const struct snd_kcontrol_new alc269_lifebook_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_acer_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - }, - 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 Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x1b, 0, HDA_INPUT), - { } -}; + if (spec->num_adc_nids > 0) { + int mux = 0; + int num_adcs = 0; -static const struct snd_kcontrol_new alc269_laptop_mixer[] = { - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - { } /* end */ -}; + if (spec->input_mux && spec->input_mux->num_items > 1) + mux = 1; + if (spec->auto_mic) { + num_adcs = 1; + mux = 0; + } else if (spec->dyn_adc_switch) + num_adcs = 1; + if (!num_adcs) { + if (spec->num_adc_nids > 3) + spec->num_adc_nids = 3; + else if (!spec->num_adc_nids) + return; + num_adcs = spec->num_adc_nids; + } + spec->cap_mixer = caps[mux][num_adcs - 1]; + } +} -static const struct snd_kcontrol_new alc269vb_laptop_mixer[] = { - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - { } /* end */ -}; +/* + * Digital-beep handlers + */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define set_beep_amp(spec, nid, idx, dir) \ + ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) -static const struct snd_kcontrol_new alc269_asus_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT), - { } /* end */ +static const struct snd_pci_quirk beep_white_list[] = { + SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), + SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), + {} }; -/* capture mixer elements */ -static const struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; +static inline int has_cdefine_beep(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct snd_pci_quirk *q; + q = snd_pci_quirk_lookup(codec->bus->pci, beep_white_list); + if (q) + return q->value; + return spec->cdefine.enable_pcbeep; +} +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#define has_cdefine_beep(codec) 0 +#endif -static const struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code + */ +static int alc880_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; -static const struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc880_ignore); + if (err < 0) + return err; + if (!spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ -static const struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); + if (err < 0) + return err; -/* FSC amilo */ -#define alc269_fujitsu_mixer alc269_laptop_mixer + spec->multiout.max_channels = spec->multiout.num_dacs * 2; -static const struct hda_verb alc269_quanta_fl1_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - { } -}; + alc_auto_parse_digital(codec); -static const struct hda_verb alc269_lifebook_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - { } -}; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); -/* toggle speaker-output according to the hp-jack state */ -static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) -{ - alc_hp_automute(codec); + alc_remove_invalid_adc_nids(codec); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 0x0c); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x680); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 0x0c); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x480); + return 1; } -#define alc269_lifebook_speaker_automute \ - alc269_quanta_fl1_speaker_automute - -static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) +/* additional initialization for auto-configuration model */ +static void alc880_auto_init(struct hda_codec *codec) { - unsigned int present_laptop; - unsigned int present_dock; - - present_laptop = snd_hda_jack_detect(codec, 0x18); - present_dock = snd_hda_jack_detect(codec, 0x1b); - - /* Laptop mic port overrides dock mic port, design decision */ - if (present_dock) - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, 0x3); - if (present_laptop) - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, 0x0); - if (!present_dock && !present_laptop) - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, 0x1); + struct alc_spec *spec = codec->spec; + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); } -static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - alc269_quanta_fl1_speaker_automute(codec); - break; - case ALC880_MIC_EVENT: - alc_mic_automute(codec); - break; - } -} +#ifdef CONFIG_SND_HDA_POWER_SAVE +static const struct hda_amp_list alc880_loopbacks[] = { + { 0x0b, HDA_INPUT, 0 }, + { 0x0b, HDA_INPUT, 1 }, + { 0x0b, HDA_INPUT, 2 }, + { 0x0b, HDA_INPUT, 3 }, + { 0x0b, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif -static void alc269_lifebook_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc269_lifebook_speaker_automute(codec); - if ((res >> 26) == ALC880_MIC_EVENT) - alc269_lifebook_mic_autoswitch(codec); -} +/* + * board setups + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#define alc_board_config \ + snd_hda_check_board_config +#define alc_board_codec_sid_config \ + snd_hda_check_board_codec_sid_config +#include "alc_quirks.c" +#else +#define alc_board_config(codec, nums, models, tbl) -1 +#define alc_board_codec_sid_config(codec, nums, models, tbl) -1 +#define setup_preset(codec, x) /* NOP */ +#endif -static void alc269_quanta_fl1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; -} +/* + * OK, here we have finally the patch for ALC880 + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc880_quirks.c" +#endif -static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) +static int patch_alc880(struct hda_codec *codec) { - alc269_quanta_fl1_speaker_automute(codec); - alc_mic_automute(codec); -} + struct alc_spec *spec; + int board_config; + int err; -static void alc269_lifebook_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.hp_pins[1] = 0x1a; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; -} + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; -static void alc269_lifebook_init_hook(struct hda_codec *codec) -{ - alc269_lifebook_speaker_automute(codec); - alc269_lifebook_mic_autoswitch(codec); -} + codec->spec = spec; -static const struct hda_verb alc269_laptop_dmic_init_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + spec->mixer_nid = 0x0b; -static const struct hda_verb alc269_laptop_amic_init_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + board_config = alc_board_config(codec, ALC880_MODEL_LAST, + alc880_models, alc880_cfg_tbl); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC_MODEL_AUTO; + } -static const struct hda_verb alc269vb_laptop_dmic_init_verbs[] = { - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x06}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + if (board_config == ALC_MODEL_AUTO) { + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using 3-stack mode...\n"); + board_config = ALC880_3ST; + } +#endif + } -static const struct hda_verb alc269vb_laptop_amic_init_verbs[] = { - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } -static const struct hda_verb alc271_acer_dmic_verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x22, AC_VERB_SET_CONNECT_SEL, 6}, - { } -}; + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc880_presets[board_config]); -static void alc269_laptop_amic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; -} + if (!spec->adc_nids && spec->input_mux) { + alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); + alc_remove_invalid_adc_nids(codec); + } + set_capture_mixer(codec); + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); -static void alc269_laptop_dmic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; -} + spec->vmaster_nid = 0x0c; -static void alc269vb_laptop_amic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; -} + codec->patch_ops = alc_patch_ops; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc880_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc880_loopbacks; +#endif -static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; + return 0; } + /* - * generic initialization of ADC, input mixers and output mixers + * ALC260 support */ -static const struct hda_verb alc269_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* - * Set up output mixers (0x02 - 0x03) - */ - /* 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}, - - /* FIXME: use Mux-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* set EAPD */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; +/* convert from pin to volume-mixer widget */ +static hda_nid_t alc260_pin_to_vol_mix(hda_nid_t nid) +{ + if (nid >= 0x0f && nid <= 0x11) + return nid - 0x7; + else if (nid >= 0x12 && nid <= 0x15) + return 0x08; + else + return 0; +} -static const struct hda_verb alc269vb_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, + const char *pfx, int *vol_bits) +{ + hda_nid_t nid_vol; + unsigned long vol_val, sw_val; + int chs, err; - /* - * Set up output mixers (0x02 - 0x03) - */ - /* 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)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x21, 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}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* FIXME: use Mux-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x22, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* set EAPD */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; + nid_vol = alc260_pin_to_vol_mix(nid); + if (!nid_vol) + return 0; /* N/A */ + if (nid == 0x11) + chs = 2; + else + chs = 3; + vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, chs, 0, HDA_OUTPUT); + sw_val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); -#define alc269_auto_create_multi_out_ctls \ - alc268_auto_create_multi_out_ctls + if (!(*vol_bits & (1 << nid_vol))) { + /* first control for the volume widget */ + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); + if (err < 0) + return err; + *vol_bits |= (1 << nid_vol); + } + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); + if (err < 0) + return err; + return 1; +} -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc269_loopbacks alc880_loopbacks -#endif +/* add playback controls from the parsed DAC table */ +static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int err; + int vols = 0; -static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup - }, -}; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + spec->private_dac_nids[0] = 0x02; -static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ - /* NID is set in alc_build_pcms */ -}; + nid = cfg->line_out_pins[0]; + if (nid) { + const char *pfx; + int index; + pfx = alc_get_line_out_pfx(spec, 0, true, &index); + err = alc260_add_playback_controls(spec, nid, pfx, &vols); + if (err < 0) + return err; + } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int alc269_mic2_for_mute_led(struct hda_codec *codec) -{ - switch (codec->subsystem_id) { - case 0x103c1586: - return 1; + nid = cfg->speaker_pins[0]; + if (nid) { + err = alc260_add_playback_controls(spec, nid, "Speaker", &vols); + if (err < 0) + return err; + } + + nid = cfg->hp_pins[0]; + if (nid) { + err = alc260_add_playback_controls(spec, nid, "Headphone", + &vols); + if (err < 0) + return err; } return 0; } -static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) +static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int sel_idx) { - /* update mute-LED according to the speaker mute state */ - if (nid == 0x01 || nid == 0x14) { - int pinval; - if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) & - HDA_AMP_MUTE) - pinval = 0x24; - else - pinval = 0x20; - /* mic2 vref pin is used for mute LED control */ - snd_hda_codec_update_cache(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinval); + hda_nid_t mix; + + alc_set_pin_output(codec, nid, pin_type); + /* need the manual connection? */ + if (nid >= 0x12) { + int idx = nid - 0x12; + snd_hda_codec_write(codec, idx + 0x0b, 0, + AC_VERB_SET_CONNECT_SEL, sel_idx); } - return alc_check_power_status(codec, nid); + + mix = alc260_pin_to_vol_mix(nid); + if (!mix) + return; + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); } -#endif /* CONFIG_SND_HDA_POWER_SAVE */ -/* different alc269-variants */ -enum { - ALC269_TYPE_ALC269VA, - ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC269VC, -}; +static void alc260_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; -/* - * BIOS auto configuration - */ -static int alc269_parse_auto_config(struct hda_codec *codec) + nid = spec->autocfg.line_out_pins[0]; + if (nid) { + int pin_type = get_pin_type(spec->autocfg.line_out_type); + alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0); + } + + nid = spec->autocfg.speaker_pins[0]; + if (nid) + alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); + + nid = spec->autocfg.hp_pins[0]; + if (nid) + alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); +} + +static int alc260_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int err; - static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc269_ignore); + alc260_ignore); if (err < 0) return err; - - err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; + if (!spec->kctls.list) + return 0; /* can't find valid BIOS pin config */ err = alc_auto_create_input_ctls(codec); if (err < 0) return err; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; + spec->multiout.max_channels = 2; alc_auto_parse_digital(codec); @@ -14245,32 +3781,17 @@ static int alc269_parse_auto_config(struct hda_codec *codec) alc_remove_invalid_adc_nids(codec); - if (spec->codec_variant != ALC269_TYPE_ALC269VA) - alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); - else - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); alc_auto_check_switches(codec); - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(codec); - return 1; } -#define alc269_auto_init_multi_out alc268_auto_init_multi_out -#define alc269_auto_init_hp_out alc268_auto_init_hp_out - - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc269_auto_init(struct hda_codec *codec) +/* additional initialization for auto-configuration model */ +static void alc260_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc269_auto_init_multi_out(codec); - alc269_auto_init_hp_out(codec); + alc260_auto_init_multi_out(codec); alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -14278,410 +3799,264 @@ static void alc269_auto_init(struct hda_codec *codec) alc_inithook(codec); } -static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) -{ - int val = alc_read_coef_idx(codec, 0x04); - if (power_up) - val |= 1 << 11; - else - val &= ~(1 << 11); - alc_write_coef_idx(codec, 0x04, val); -} +#ifdef CONFIG_SND_HDA_POWER_SAVE +static const struct hda_amp_list alc260_loopbacks[] = { + { 0x07, HDA_INPUT, 0 }, + { 0x07, HDA_INPUT, 1 }, + { 0x07, HDA_INPUT, 2 }, + { 0x07, HDA_INPUT, 3 }, + { 0x07, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif -static void alc269_shutup(struct hda_codec *codec) +/* + * Pin config fixes + */ +enum { + PINFIX_HP_DC5750, +}; + +static const struct alc_fixup alc260_fixups[] = { + [PINFIX_HP_DC5750] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x11, 0x90130110 }, /* speaker */ + { } + } + }, +}; + +static const struct snd_pci_quirk alc260_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750), + {} +}; + +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc260_quirks.c" +#endif + +static int patch_alc260(struct hda_codec *codec) { - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) - alc269_toggle_power_output(codec, 0); - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { - alc269_toggle_power_output(codec, 0); - msleep(150); + struct alc_spec *spec; + int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->mixer_nid = 0x07; + + board_config = alc_board_config(codec, ALC260_MODEL_LAST, + alc260_models, alc260_cfg_tbl); + if (board_config < 0) { + snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC_MODEL_AUTO; } -} -#ifdef SND_HDA_NEEDS_RESUME -static int alc269_resume(struct hda_codec *codec) -{ - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { - alc269_toggle_power_output(codec, 0); - msleep(150); + if (board_config == ALC_MODEL_AUTO) { + alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - codec->patch_ops.init(codec); + if (board_config == ALC_MODEL_AUTO) { + /* automatic parse from the BIOS config */ + err = alc260_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC260_BASIC; + } +#endif + } - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) { - alc269_toggle_power_output(codec, 1); - msleep(200); + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; } - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) - alc269_toggle_power_output(codec, 1); + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc260_presets[board_config]); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - hda_call_check_power_status(codec, 0x01); - return 0; -} -#endif /* SND_HDA_NEEDS_RESUME */ + if (!spec->adc_nids && spec->input_mux) { + alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); + alc_remove_invalid_adc_nids(codec); + } + set_capture_mixer(codec); + set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); -static void alc269_fixup_hweq(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - int coef; + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - if (action != ALC_FIXUP_ACT_INIT) - return; - coef = alc_read_coef_idx(codec, 0x1e); - alc_write_coef_idx(codec, 0x1e, coef | 0x80); -} + spec->vmaster_nid = 0x08; -static void alc271_fixup_dmic(struct hda_codec *codec, - const struct alc_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; + codec->patch_ops = alc_patch_ops; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc260_auto_init; + spec->shutup = alc_eapd_shutup; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc260_loopbacks; +#endif - if (strcmp(codec->chip_name, "ALC271X")) - return; - cfg = snd_hda_codec_get_pincfg(codec, 0x12); - if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) - snd_hda_sequence_write(codec, verbs); + return 0; } + +/* + * ALC882/883/885/888/889 support + * + * ALC882 is almost identical with ALC880 but has cleaner and more flexible + * configuration. Each pin widget can choose any input DACs and a mixer. + * Each ADC is connected from a mixer of all inputs. This makes possible + * 6-channel independent captures. + * + * In addition, an independent DAC for the multi-playback (not used in this + * driver yet). + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc882_loopbacks alc880_loopbacks +#endif + +/* + * Pin config fixes + */ enum { - ALC269_FIXUP_SONY_VAIO, - ALC275_FIXUP_SONY_VAIO_GPIO2, - ALC269_FIXUP_DELL_M101Z, - ALC269_FIXUP_SKU_IGNORE, - ALC269_FIXUP_ASUS_G73JW, - ALC269_FIXUP_LENOVO_EAPD, - ALC275_FIXUP_SONY_HWEQ, - ALC271_FIXUP_DMIC, + PINFIX_ABIT_AW9D_MAX, + PINFIX_LENOVO_Y530, + PINFIX_PB_M5210, + PINFIX_ACER_ASPIRE_7736, }; -static const struct alc_fixup alc269_fixups[] = { - [ALC269_FIXUP_SONY_VAIO] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, - {} - } - }, - [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, +static const struct alc_fixup alc882_fixups[] = { + [PINFIX_ABIT_AW9D_MAX] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x15, 0x01080104 }, /* side */ + { 0x16, 0x01011012 }, /* rear */ + { 0x17, 0x01016011 }, /* clfe */ { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_SONY_VAIO - }, - [ALC269_FIXUP_DELL_M101Z] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 13}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, - {} } }, - [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, - }, - [ALC269_FIXUP_ASUS_G73JW] = { + [PINFIX_LENOVO_Y530] = { .type = ALC_FIXUP_PINS, .v.pins = (const struct alc_pincfg[]) { - { 0x17, 0x99130111 }, /* subwoofer */ + { 0x15, 0x99130112 }, /* rear int speakers */ + { 0x16, 0x99130111 }, /* subwoofer */ { } } }, - [ALC269_FIXUP_LENOVO_EAPD] = { + [PINFIX_PB_M5210] = { .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} } }, - [ALC275_FIXUP_SONY_HWEQ] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc269_fixup_hweq, - .chained = true, - .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 - }, - [ALC271_FIXUP_DMIC] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc271_fixup_dmic, + [PINFIX_ACER_ASPIRE_7736] = { + .type = ALC_FIXUP_SKU, + .v.sku = ALC_FIXUP_SKU_IGNORE, }, }; -static const struct snd_pci_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), - SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), - SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), +static const struct snd_pci_quirk alc882_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), + SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), + SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), {} }; - /* - * configuration and preset + * BIOS auto configuration */ -static const char * const alc269_models[ALC269_MODEL_LAST] = { - [ALC269_BASIC] = "basic", - [ALC269_QUANTA_FL1] = "quanta", - [ALC269_AMIC] = "laptop-amic", - [ALC269_DMIC] = "laptop-dmic", - [ALC269_FUJITSU] = "fujitsu", - [ALC269_LIFEBOOK] = "lifebook", - [ALC269_AUTO] = "auto", -}; +/* almost identical with ALC880 parser... */ +static int alc882_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; + int err; -static const struct snd_pci_quirk alc269_cfg_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), - SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER), - SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", - ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", - ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO), - SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), - SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), - SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), - SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC), - SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC), - SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC), - {} -}; + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc882_ignore); + if (err < 0) + return err; + if (!spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ -static const struct alc_config_preset alc269_presets[] = { - [ALC269_BASIC] = { - .mixers = { alc269_base_mixer }, - .init_verbs = { alc269_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - }, - [ALC269_QUANTA_FL1] = { - .mixers = { alc269_quanta_fl1_mixer }, - .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - .unsol_event = alc269_quanta_fl1_unsol_event, - .setup = alc269_quanta_fl1_setup, - .init_hook = alc269_quanta_fl1_init_hook, - }, - [ALC269_AMIC] = { - .mixers = { alc269_laptop_mixer }, - .cap_mixer = alc269_laptop_analog_capture_mixer, - .init_verbs = { alc269_init_verbs, - alc269_laptop_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269_laptop_amic_setup, - .init_hook = alc_inithook, - }, - [ALC269_DMIC] = { - .mixers = { alc269_laptop_mixer }, - .cap_mixer = alc269_laptop_digital_capture_mixer, - .init_verbs = { alc269_init_verbs, - alc269_laptop_dmic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269_laptop_dmic_setup, - .init_hook = alc_inithook, - }, - [ALC269VB_AMIC] = { - .mixers = { alc269vb_laptop_mixer }, - .cap_mixer = alc269vb_laptop_analog_capture_mixer, - .init_verbs = { alc269vb_init_verbs, - alc269vb_laptop_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269vb_laptop_amic_setup, - .init_hook = alc_inithook, - }, - [ALC269VB_DMIC] = { - .mixers = { alc269vb_laptop_mixer }, - .cap_mixer = alc269vb_laptop_digital_capture_mixer, - .init_verbs = { alc269vb_init_verbs, - alc269vb_laptop_dmic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269vb_laptop_dmic_setup, - .init_hook = alc_inithook, - }, - [ALC269_FUJITSU] = { - .mixers = { alc269_fujitsu_mixer }, - .cap_mixer = alc269_laptop_digital_capture_mixer, - .init_verbs = { alc269_init_verbs, - alc269_laptop_dmic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269_laptop_dmic_setup, - .init_hook = alc_inithook, - }, - [ALC269_LIFEBOOK] = { - .mixers = { alc269_lifebook_mixer }, - .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - .unsol_event = alc269_lifebook_unsol_event, - .setup = alc269_lifebook_setup, - .init_hook = alc269_lifebook_init_hook, - }, - [ALC271_ACER] = { - .mixers = { alc269_asus_mixer }, - .cap_mixer = alc269vb_laptop_digital_capture_mixer, - .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .adc_nids = alc262_dmic_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids), - .capsrc_nids = alc262_dmic_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - .dig_out_nid = ALC880_DIGOUT_NID, - .unsol_event = alc_sku_unsol_event, - .setup = alc269vb_laptop_dmic_setup, - .init_hook = alc_inithook, - }, -}; + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); + if (err < 0) + return err; -static int alc269_fill_coef(struct hda_codec *codec) -{ - int val; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8817); - } + alc_auto_parse_digital(codec); - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8814); - } + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) { - val = alc_read_coef_idx(codec, 0x04); - /* Power up output pin */ - alc_write_coef_idx(codec, 0x04, val | (1<<11)); - } + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; - if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { - val = alc_read_coef_idx(codec, 0xd); - if ((val & 0x0c00) >> 10 != 0x1) { - /* Capless ramp up clock control */ - alc_write_coef_idx(codec, 0xd, val | (1<<10)); - } - val = alc_read_coef_idx(codec, 0x17); - if ((val & 0x01c0) >> 6 != 0x4) { - /* Class D power on reset */ - alc_write_coef_idx(codec, 0x17, val | (1<<7)); - } - } + alc_remove_invalid_adc_nids(codec); - val = alc_read_coef_idx(codec, 0xd); /* Class D */ - alc_write_coef_idx(codec, 0xd, val | (1<<14)); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); - val = alc_read_coef_idx(codec, 0x4); /* HP */ - alc_write_coef_idx(codec, 0x4, val | (1<<11)); + return 1; /* config found */ +} - return 0; +/* additional initialization for auto-configuration model */ +static void alc882_auto_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); } -static int patch_alc269(struct hda_codec *codec) +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc882_quirks.c" +#endif + +static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; - int board_config, coef; - int err; + int err, board_config; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -14691,68 +4066,51 @@ static int patch_alc269(struct hda_codec *codec) spec->mixer_nid = 0x0b; - alc_auto_parse_customize_define(codec); - - if (codec->vendor_id == 0x10ec0269) { - spec->codec_variant = ALC269_TYPE_ALC269VA; - coef = alc_read_coef_idx(codec, 0); - if ((coef & 0x00f0) == 0x0010) { - if (codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) { - alc_codec_rename(codec, "ALC271X"); - } else if ((coef & 0xf000) == 0x2000) { - alc_codec_rename(codec, "ALC259"); - } else if ((coef & 0xf000) == 0x3000) { - alc_codec_rename(codec, "ALC258"); - } else if ((coef & 0xfff0) == 0x3010) { - alc_codec_rename(codec, "ALC277"); - } else { - alc_codec_rename(codec, "ALC269VB"); - } - spec->codec_variant = ALC269_TYPE_ALC269VB; - } else if ((coef & 0x00f0) == 0x0020) { - if (coef == 0xa023) - alc_codec_rename(codec, "ALC259"); - else if (coef == 0x6023) - alc_codec_rename(codec, "ALC281X"); - else if (codec->bus->pci->subsystem_vendor == 0x17aa && - codec->bus->pci->subsystem_device == 0x21f3) - alc_codec_rename(codec, "ALC3202"); - else - alc_codec_rename(codec, "ALC269VC"); - spec->codec_variant = ALC269_TYPE_ALC269VC; - } else - alc_fix_pll_init(codec, 0x20, 0x04, 15); - alc269_fill_coef(codec); + switch (codec->vendor_id) { + case 0x10ec0882: + case 0x10ec0885: + break; + default: + /* ALC883 and variants */ + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + break; } - board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, - alc269_models, - alc269_cfg_tbl); + board_config = alc_board_config(codec, ALC882_MODEL_LAST, + alc882_models, alc882_cfg_tbl); + + if (board_config < 0) + board_config = alc_board_codec_sid_config(codec, + ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC269_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC269_AUTO) { - alc_pick_fixup(codec, NULL, alc269_fixup_tbl, alc269_fixups); + if (board_config == ALC_MODEL_AUTO) { + alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - if (board_config == ALC269_AUTO) { + alc_auto_parse_customize_define(codec); + + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ - err = alc269_parse_auto_config(codec); + err = alc882_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); - board_config = ALC269_BASIC; + board_config = ALC882_3ST_DIG; } +#endif } if (has_cdefine_beep(codec)) { @@ -14763,804 +4121,173 @@ static int patch_alc269(struct hda_codec *codec) } } - if (board_config != ALC269_AUTO) - setup_preset(codec, &alc269_presets[board_config]); - - if (board_config == ALC269_QUANTA_FL1) { - /* Due to a hardware problem on Lenovo Ideadpad, we need to - * fix the sample rate of analog I/O to 44.1kHz - */ - spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; - } + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc882_presets[board_config]); - if (!spec->adc_nids) { /* wasn't filled automatically? use default */ + if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer) - set_capture_mixer(codec); + set_capture_mixer(codec); + if (has_cdefine_beep(codec)) - set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - spec->vmaster_nid = 0x02; + spec->vmaster_nid = 0x0c; codec->patch_ops = alc_patch_ops; -#ifdef SND_HDA_NEEDS_RESUME - codec->patch_ops.resume = alc269_resume; -#endif - if (board_config == ALC269_AUTO) - spec->init_hook = alc269_auto_init; - spec->shutup = alc269_shutup; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc882_auto_init; alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) - spec->loopback.amplist = alc269_loopbacks; - if (alc269_mic2_for_mute_led(codec)) - codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps; + spec->loopback.amplist = alc882_loopbacks; #endif return 0; } -/* - * ALC861 channel source setting (2/6 channel selection for 3-stack) - */ - -/* - * set the path ways for 2 channel output - * need to set the codec line out and mic 1 pin widgets to inputs - */ -static const struct hda_verb alc861_threestack_ch2_init[] = { - /* set pin widget 1Ah (line in) for input */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* set pin widget 18h (mic1/2) for input, for mic also enable - * the vref - */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ -#endif - { } /* end */ -}; /* - * 6ch mode - * need to set the codec line out and mic 1 pin widgets to outputs + * ALC262 support */ -static const struct hda_verb alc861_threestack_ch6_init[] = { - /* set pin widget 1Ah (line in) for output (Back Surround)*/ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* set pin widget 18h (mic1) for output (CLFE)*/ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - - { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, - - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ -#endif - { } /* end */ -}; - -static const struct hda_channel_mode alc861_threestack_modes[2] = { - { 2, alc861_threestack_ch2_init }, - { 6, alc861_threestack_ch6_init }, -}; -/* Set mic1 as input and unmute the mixer */ -static const struct hda_verb alc861_uniwill_m31_ch2_init[] = { - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ - { } /* end */ -}; -/* Set mic1 as output and mute mixer */ -static const struct hda_verb alc861_uniwill_m31_ch4_init[] = { - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ - { } /* end */ -}; - -static const struct hda_channel_mode alc861_uniwill_m31_modes[2] = { - { 2, alc861_uniwill_m31_ch2_init }, - { 4, alc861_uniwill_m31_ch4_init }, -}; - -/* Set mic1 and line-in as input and unmute the mixer */ -static const struct hda_verb alc861_asus_ch2_init[] = { - /* set pin widget 1Ah (line in) for input */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* set pin widget 18h (mic1/2) for input, for mic also enable - * the vref - */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ -#endif - { } /* end */ -}; -/* Set mic1 nad line-in as output and mute mixer */ -static const struct hda_verb alc861_asus_ch6_init[] = { - /* set pin widget 1Ah (line in) for output (Back Surround)*/ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ - /* set pin widget 18h (mic1) for output (CLFE)*/ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ - { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, - - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ -#endif - { } /* end */ -}; - -static const struct hda_channel_mode alc861_asus_modes[2] = { - { 2, alc861_asus_ch2_init }, - { 6, alc861_asus_ch6_init }, -}; - -/* patch-ALC861 */ - -static const struct snd_kcontrol_new alc861_base_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), - - /*Input mixer control */ - /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_3ST_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ - - /* Input mixer control */ - /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - .private_value = ARRAY_SIZE(alc861_threestack_modes), - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_toshiba_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ - - /* Input mixer control */ - /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes), - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_asus_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), - - /* Input mixer control */ - HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - .private_value = ARRAY_SIZE(alc861_asus_modes), - }, - { } -}; - -/* additional mixer */ -static const struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - { } -}; - -/* - * generic initialization of ADC, input mixers and output mixers +/* We use two mixers depending on the output pin; 0x16 is a mono output + * and thus it's bound with a different mixer. + * This function returns which mixer amp should be used. */ -static const struct hda_verb alc861_base_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute DAC0~3 & spdif out*/ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - - { } -}; - -static const struct hda_verb alc861_threestack_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute DAC0~3 & spdif out*/ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - { } -}; - -static const struct hda_verb alc861_uniwill_m31_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - /* this has to be set to VREF80 */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute DAC0~3 & spdif out*/ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - { } -}; - -static const struct hda_verb alc861_asus_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) - * according to codec#0 this is the HP jack - */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */ - /* route front PCM to HP */ - { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - /* this has to be set to VREF80 */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute DAC0~3 & spdif out*/ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - { } -}; - -/* additional init verbs for ASUS laptops */ -static const struct hda_verb alc861_asus_laptop_init_verbs[] = { - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */ - { } -}; - -static const struct hda_verb alc861_toshiba_init_verbs[] = { - {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc861_toshiba_automute(struct hda_codec *codec) -{ - unsigned int present = snd_hda_jack_detect(codec, 0x0f); - - snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, - HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); -} - -static void alc861_toshiba_unsol_event(struct hda_codec *codec, - unsigned int res) +static int alc262_check_volbit(hda_nid_t nid) { - if ((res >> 26) == ALC880_HP_EVENT) - alc861_toshiba_automute(codec); + if (!nid) + return 0; + else if (nid == 0x16) + return 2; + else + return 1; } -#define ALC861_DIGOUT_NID 0x07 - -static const struct hda_channel_mode alc861_8ch_modes[1] = { - { 8, NULL } -}; - -static const hda_nid_t alc861_dac_nids[4] = { - /* front, surround, clfe, side */ - 0x03, 0x06, 0x05, 0x04 -}; - -static const hda_nid_t alc660_dac_nids[3] = { - /* front, clfe, surround */ - 0x03, 0x05, 0x06 -}; - -static const hda_nid_t alc861_adc_nids[1] = { - /* ADC0-2 */ - 0x08, -}; - -static const struct hda_input_mux alc861_capture_source = { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x3 }, - { "Line", 0x1 }, - { "CD", 0x4 }, - { "Mixer", 0x5 }, - }, -}; - -static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) +static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, + const char *pfx, int *vbits, int idx) { - struct alc_spec *spec = codec->spec; - hda_nid_t mix, srcs[5]; - int i, num; + unsigned long val; + int vbit; - if (snd_hda_get_connections(codec, pin, &mix, 1) != 1) + vbit = alc262_check_volbit(nid); + if (!vbit) return 0; - num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); - if (num < 0) + if (*vbits & vbit) /* a volume control for this mixer already there */ return 0; - for (i = 0; i < num; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, srcs[i])); - if (type != AC_WID_AUD_OUT) - continue; - if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids, - spec->multiout.num_dacs)) - return srcs[i]; - } - return 0; + *vbits |= vbit; + if (vbit == 2) + val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); + return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val); } -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc861_auto_fill_dac_nids(struct hda_codec *codec) +static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, + const char *pfx, int idx) { - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - hda_nid_t nid, dac; - - spec->multiout.dac_nids = spec->private_dac_nids; - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - dac = alc861_look_for_dac(codec, nid); - if (!dac) - continue; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - return 0; -} + unsigned long val; -static int __alc861_create_out_sw(struct hda_codec *codec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) -{ - return __add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (!nid) + return 0; + if (nid == 0x16) + val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val); } -#define alc861_create_out_sw(codec, pfx, nid, chs) \ - __alc861_create_out_sw(codec, pfx, nid, 0, chs) - /* add playback controls from the parsed DAC table */ -static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, +static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - int i, err, noutputs; + const char *pfx; + int vbits; + int i, index, err; - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; + spec->multiout.num_dacs = 1; /* only use one dac */ + spec->multiout.dac_nids = spec->private_dac_nids; + spec->private_dac_nids[0] = 2; - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - nid = spec->multiout.dac_nids[i]; - if (!nid) - continue; - name = alc_get_line_out_pfx(spec, i, true, &index); - if (!name) { - /* Center/LFE */ - err = alc861_create_out_sw(codec, "Center", nid, 1); - if (err < 0) - return err; - err = alc861_create_out_sw(codec, "LFE", nid, 2); + for (i = 0; i < 2; i++) { + pfx = alc_get_line_out_pfx(spec, i, true, &index); + if (!pfx) + pfx = "PCM"; + err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, + index); + if (err < 0) + return err; + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i], + "Speaker", i); if (err < 0) return err; - } else { - err = __alc861_create_out_sw(codec, name, nid, index, 3); + } + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i], + "Headphone", i); if (err < 0) return err; } } - return 0; -} - -static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int err; - hda_nid_t nid; - - if (!pin) - return 0; - if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) { - nid = alc861_look_for_dac(codec, pin); - if (nid) { - err = alc861_create_out_sw(codec, "Headphone", nid, 3); + vbits = alc262_check_volbit(cfg->line_out_pins[0]) | + alc262_check_volbit(cfg->speaker_pins[0]) | + alc262_check_volbit(cfg->hp_pins[0]); + vbits = 0; + for (i = 0; i < 2; i++) { + pfx = alc_get_line_out_pfx(spec, i, true, &index); + if (!pfx) + pfx = "PCM"; + err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx, + &vbits, i); + if (err < 0) + return err; + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i], + "Speaker", &vbits, i); + if (err < 0) + return err; + } + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i], + "Headphone", &vbits, i); if (err < 0) return err; - spec->multiout.hp_nid = nid; } } return 0; } -static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, - int pin_type, hda_nid_t dac) -{ - hda_nid_t mix, srcs[5]; - int i, num; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - if (snd_hda_get_connections(codec, nid, &mix, 1) != 1) - return; - num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); - if (num < 0) - return; - for (i = 0; i < num; i++) { - unsigned int mute; - if (srcs[i] == dac || srcs[i] == 0x15) - mute = AMP_IN_UNMUTE(i); - else - mute = AMP_IN_MUTE(i); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - mute); - } -} - -static void alc861_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc861_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} - -static void alc861_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->autocfg.hp_outs) - alc861_auto_set_output_and_unmute(codec, - spec->autocfg.hp_pins[0], - PIN_HP, - spec->multiout.hp_nid); - if (spec->autocfg.speaker_outs) - alc861_auto_set_output_and_unmute(codec, - spec->autocfg.speaker_pins[0], - PIN_OUT, - spec->multiout.dac_nids[0]); -} - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code +/* + * BIOS auto configuration */ -static int alc861_parse_auto_config(struct hda_codec *codec) +static int alc262_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int err; - static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc861_ignore); + alc262_ignore); if (err < 0) return err; - if (!spec->autocfg.line_outs) + 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 = alc861_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); + } + err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); @@ -15569,212 +4296,82 @@ static int alc861_parse_auto_config(struct hda_codec *codec) spec->multiout.max_channels = spec->multiout.num_dacs * 2; + dig_only: alc_auto_parse_digital(codec); if (spec->kctls.list) add_mixer(spec, spec->kctls.list); + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + alc_remove_invalid_adc_nids(codec); - alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); alc_auto_check_switches(codec); - set_capture_mixer(codec); - return 1; } -/* additional initialization for auto-configuration model */ -static void alc861_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc861_auto_init_multi_out(codec); - alc861_auto_init_hp_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc861_loopbacks[] = { - { 0x15, HDA_INPUT, 0 }, - { 0x15, HDA_INPUT, 1 }, - { 0x15, HDA_INPUT, 2 }, - { 0x15, HDA_INPUT, 3 }, - { } /* end */ -}; -#endif - - /* - * configuration and preset + * Pin config fixes */ -static const char * const alc861_models[ALC861_MODEL_LAST] = { - [ALC861_3ST] = "3stack", - [ALC660_3ST] = "3stack-660", - [ALC861_3ST_DIG] = "3stack-dig", - [ALC861_6ST_DIG] = "6stack-dig", - [ALC861_UNIWILL_M31] = "uniwill-m31", - [ALC861_TOSHIBA] = "toshiba", - [ALC861_ASUS] = "asus", - [ALC861_ASUS_LAPTOP] = "asus-laptop", - [ALC861_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc861_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), - SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), - SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), - SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), - /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) - * Any other models that need this preset? - */ - /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ - SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), - SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), - SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), - SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), - SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), - /* FIXME: the below seems conflict */ - /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ - SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), - SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), - {} -}; - -static const struct alc_config_preset alc861_presets[] = { - [ALC861_3ST] = { - .mixers = { alc861_3ST_mixer }, - .init_verbs = { alc861_threestack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), - .channel_mode = alc861_threestack_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_3ST_DIG] = { - .mixers = { alc861_base_mixer }, - .init_verbs = { alc861_threestack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), - .channel_mode = alc861_threestack_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_6ST_DIG] = { - .mixers = { alc861_base_mixer }, - .init_verbs = { alc861_base_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes), - .channel_mode = alc861_8ch_modes, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC660_3ST] = { - .mixers = { alc861_3ST_mixer }, - .init_verbs = { alc861_threestack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc660_dac_nids), - .dac_nids = alc660_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), - .channel_mode = alc861_threestack_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_UNIWILL_M31] = { - .mixers = { alc861_uniwill_m31_mixer }, - .init_verbs = { alc861_uniwill_m31_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes), - .channel_mode = alc861_uniwill_m31_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_TOSHIBA] = { - .mixers = { alc861_toshiba_mixer }, - .init_verbs = { alc861_base_init_verbs, - alc861_toshiba_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - .unsol_event = alc861_toshiba_unsol_event, - .init_hook = alc861_toshiba_automute, - }, - [ALC861_ASUS] = { - .mixers = { alc861_asus_mixer }, - .init_verbs = { alc861_asus_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_asus_modes), - .channel_mode = alc861_asus_modes, - .need_dac_fix = 1, - .hp_nid = 0x06, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_ASUS_LAPTOP] = { - .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer }, - .init_verbs = { alc861_asus_init_verbs, - alc861_asus_laptop_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, -}; - -/* Pin config fixes */ enum { - PINFIX_FSC_AMILO_PI1505, + PINFIX_FSC_H270, + PINFIX_HP_Z200, }; -static const struct alc_fixup alc861_fixups[] = { - [PINFIX_FSC_AMILO_PI1505] = { +static const struct alc_fixup alc262_fixups[] = { + [PINFIX_FSC_H270] = { .type = ALC_FIXUP_PINS, .v.pins = (const struct alc_pincfg[]) { - { 0x0b, 0x0221101f }, /* HP */ - { 0x0f, 0x90170310 }, /* speaker */ + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0221142f }, /* front HP */ + { 0x1b, 0x0121141f }, /* rear HP */ + { } + } + }, + [PINFIX_HP_Z200] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x16, 0x99130120 }, /* internal speaker */ { } } }, }; -static const struct snd_pci_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), +static const struct snd_pci_quirk alc262_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", PINFIX_HP_Z200), + SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270), {} }; -static int patch_alc861(struct hda_codec *codec) + +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc262_loopbacks alc880_loopbacks +#endif + +/* init callback for auto-configuration model -- overriding the default init */ +static void alc262_auto_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); +} + +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc262_quirks.c" +#endif + +static int patch_alc262(struct hda_codec *codec) { struct alc_spec *spec; int board_config; @@ -15786,863 +4383,379 @@ static int patch_alc861(struct hda_codec *codec) codec->spec = spec; - spec->mixer_nid = 0x15; + spec->mixer_nid = 0x0b; + +#if 0 + /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is + * under-run + */ + { + int tmp; + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); + tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); + } +#endif + alc_auto_parse_customize_define(codec); - board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST, - alc861_models, - alc861_cfg_tbl); + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + + board_config = alc_board_config(codec, ALC262_MODEL_LAST, + alc262_models, alc262_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC861_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC861_AUTO) { - alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + if (board_config == ALC_MODEL_AUTO) { + alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - if (board_config == ALC861_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ - err = alc861_parse_auto_config(codec); + err = alc262_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); - board_config = ALC861_3ST_DIG; + board_config = ALC262_BASIC; } +#endif } - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) { - alc_free(codec); - return err; + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } } - if (board_config != ALC861_AUTO) - setup_preset(codec, &alc861_presets[board_config]); + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc262_presets[board_config]); - if (!spec->adc_nids) { + if (!spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - - if (!spec->cap_mixer) + if (!spec->cap_mixer && !spec->no_analog) set_capture_mixer(codec); - set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); - - spec->vmaster_nid = 0x03; + if (!spec->no_analog && has_cdefine_beep(codec)) + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; - if (board_config == ALC861_AUTO) { - spec->init_hook = alc861_auto_init; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->power_hook = alc_power_eapd; -#endif - } + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc262_auto_init; + spec->shutup = alc_eapd_shutup; + + alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) - spec->loopback.amplist = alc861_loopbacks; + spec->loopback.amplist = alc262_loopbacks; #endif return 0; } /* - * ALC861-VD support - * - * Based on ALC882 - * - * In addition, an independent DAC - */ -#define ALC861VD_DIGOUT_NID 0x06 - -static const hda_nid_t alc861vd_dac_nids[4] = { - /* front, surr, clfe, side surr */ - 0x02, 0x03, 0x04, 0x05 -}; - -/* dac_nids for ALC660vd are in a different order - according to - * Realtek's driver. - * This should probably result in a different mixer for 6stack models - * of ALC660vd codecs, but for now there is only 3stack mixer - * - and it is the same as in 861vd. - * adc_nids in ALC660vd are (is) the same as in 861vd - */ -static const hda_nid_t alc660vd_dac_nids[3] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x04, 0x03 -}; - -static const hda_nid_t alc861vd_adc_nids[1] = { - /* ADC0 */ - 0x09, -}; - -static const hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; - -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ -static const struct hda_input_mux alc861vd_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc861vd_dallas_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; - -static const struct hda_input_mux alc861vd_hp_capture_source = { - .num_items = 2, - .items = { - { "Front Mic", 0x0 }, - { "ATAPI Mic", 0x1 }, - }, -}; - -/* - * 2ch mode - */ -static const struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { - { 2, NULL } -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc861vd_6stack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc861vd_6stack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -static const struct hda_channel_mode alc861vd_6stack_modes[2] = { - { 6, alc861vd_6stack_ch6_init }, - { 8, alc861vd_6stack_ch8_init }, -}; - -static const struct snd_kcontrol_new alc861vd_chmode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ -static const struct snd_kcontrol_new alc861vd_6st_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861vd_3st_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/ - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - - { } /* end */ -}; - -/* Pin assignment: Speaker=0x14, HP = 0x15, - * Mic=0x18, Internal Mic = 0x19, CD = 0x1c, PC Beep = 0x1d - */ -static const struct snd_kcontrol_new alc861vd_dallas_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -/* Pin assignment: Speaker=0x14, Line-out = 0x15, - * Front Mic=0x18, ATAPI Mic = 0x19, - */ -static const struct snd_kcontrol_new alc861vd_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - { } /* end */ -}; - -/* - * generic initialization of ADC, input mixers and output mixers + * ALC268 */ -static const struct hda_verb alc861vd_volume_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +/* create input playback/capture controls for the given pin */ +static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, + const char *ctlname, int idx) +{ + hda_nid_t dac; + int err; - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of - * the analog-loopback mixer widget - */ - /* 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)}, - - /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + switch (nid) { + case 0x14: + case 0x16: + dac = 0x02; + break; + case 0x15: + case 0x1a: /* ALC259/269 only */ + case 0x1b: /* ALC259/269 only */ + case 0x21: /* ALC269vb has this pin, too */ + dac = 0x03; + break; + default: + snd_printd(KERN_WARNING "hda_codec: " + "ignoring pin 0x%x as unknown\n", nid); + 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; + spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + } - /* - * Set up output mixers (0x02 - 0x05) - */ - /* 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}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x05, 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_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + if (nid != 0x16) + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); + else /* mono */ + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, + HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); + if (err < 0) + return err; + return 0; +} - { } -}; +/* add playback controls from the parsed DAC table */ +static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int err; -/* - * 3-stack pin configuration: - * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b - */ -static const struct hda_verb alc861vd_3stack_init_verbs[] = { - /* - * Set pin mode and muting - */ - /* set front pin widgets 0x14 for output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line-2 In: Headphone output (output 0 - 0x0c) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + spec->multiout.dac_nids = spec->private_dac_nids; - { } -}; + nid = cfg->line_out_pins[0]; + if (nid) { + const char *name; + int index; + name = alc_get_line_out_pfx(spec, 0, true, &index); + err = alc268_new_analog_output(spec, nid, name, 0); + if (err < 0) + return err; + } -/* - * 6-stack pin configuration: - */ -static const struct hda_verb alc861vd_6stack_init_verbs[] = { - /* - * Set pin mode and muting - */ - /* set front pin widgets 0x14 for output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Rear Pin: output 1 (0x0d) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* Side Pin: output 3 (0x0f) */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line-2 In: Headphone output (output 0 - 0x0c) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + nid = cfg->speaker_pins[0]; + if (nid == 0x1d) { + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } else if (nid) { + err = alc268_new_analog_output(spec, nid, "Speaker", 0); + if (err < 0) + return err; + } + nid = cfg->hp_pins[0]; + if (nid) { + err = alc268_new_analog_output(spec, nid, "Headphone", 0); + if (err < 0) + return err; + } - { } -}; + nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; + if (nid == 0x16) { + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + return 0; +} -static const struct hda_verb alc861vd_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; +static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type) +{ + int idx; -static const struct hda_verb alc660vd_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; + alc_set_pin_output(codec, nid, pin_type); + if (snd_hda_get_conn_list(codec, nid, NULL) <= 1) + return; + if (nid == 0x14 || nid == 0x16) + idx = 0; + else + idx = 1; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); +} -static const struct hda_verb alc861vd_lenovo_unsol_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {} -}; +static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); +} -static void alc861vd_lenovo_setup(struct hda_codec *codec) +static void alc268_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} + int i; -static void alc861vd_lenovo_init_hook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc88x_simple_mic_automute(codec); + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + int pin_type = get_pin_type(spec->autocfg.line_out_type); + alc268_auto_set_output_and_unmute(codec, nid, pin_type); + } + /* mute DACs */ + for (i = 0; i < spec->multiout.num_dacs; i++) + alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]); } -static void alc861vd_lenovo_unsol_event(struct hda_codec *codec, - unsigned int res) +static void alc268_auto_init_hp_out(struct hda_codec *codec) { - switch (res >> 26) { - case ALC880_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + int i; + + for (i = 0; i < spec->autocfg.hp_outs; i++) { + pin = spec->autocfg.hp_pins[i]; + alc268_auto_set_output_and_unmute(codec, pin, PIN_HP); + } + for (i = 0; i < spec->autocfg.speaker_outs; i++) { + pin = spec->autocfg.speaker_pins[i]; + alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT); } + if (spec->autocfg.mono_out_pin) + snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* mute DACs */ + alc268_auto_init_dac(codec, spec->multiout.hp_nid); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) + alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); } -static const struct hda_verb alc861vd_dallas_verbs[] = { - {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}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - {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_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc861vd_dallas_setup(struct hda_codec *codec) +static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; + hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; + hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; + unsigned int dac_vol1, dac_vol2; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} + if (line_nid == 0x1d || speaker_nid == 0x1d) { + snd_hda_codec_write(codec, speaker_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* mute mixer inputs from 0x1d */ + snd_hda_codec_write(codec, 0x0f, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + } else { + /* unmute mixer inputs from 0x1d */ + snd_hda_codec_write(codec, 0x0f, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); + } -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc861vd_loopbacks alc880_loopbacks -#endif + dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */ + if (line_nid == 0x14) + dac_vol2 = AMP_OUT_ZERO; + else if (line_nid == 0x15) + dac_vol1 = AMP_OUT_ZERO; + if (hp_nid == 0x14) + dac_vol2 = AMP_OUT_ZERO; + else if (hp_nid == 0x15) + dac_vol1 = AMP_OUT_ZERO; + if (line_nid != 0x16 || hp_nid != 0x16 || + spec->autocfg.line_out_pins[1] != 0x16 || + spec->autocfg.line_out_pins[2] != 0x16) + dac_vol1 = dac_vol2 = AMP_OUT_ZERO; -/* - * configuration and preset - */ -static const char * const alc861vd_models[ALC861VD_MODEL_LAST] = { - [ALC660VD_3ST] = "3stack-660", - [ALC660VD_3ST_DIG] = "3stack-660-digout", - [ALC660VD_ASUS_V1S] = "asus-v1s", - [ALC861VD_3ST] = "3stack", - [ALC861VD_3ST_DIG] = "3stack-digout", - [ALC861VD_6ST_DIG] = "6stack-digout", - [ALC861VD_LENOVO] = "lenovo", - [ALC861VD_DALLAS] = "dallas", - [ALC861VD_HP] = "hp", - [ALC861VD_AUTO] = "auto", + snd_hda_codec_write(codec, 0x02, 0, + AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1); + snd_hda_codec_write(codec, 0x03, 0, + AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); +} + +/* bind Beep switches of both NID 0x0f and 0x10 */ +static const struct hda_bind_ctls alc268_bind_beep_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), + 0 + }, }; -static const struct snd_pci_quirk alc861vd_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), - SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), - /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */ - SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), - SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), - SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), - /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ - SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), - SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), - {} +static const struct snd_kcontrol_new alc268_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), + HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), + { } }; -static const struct alc_config_preset alc861vd_presets[] = { - [ALC660VD_3ST] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC660VD_3ST_DIG] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_3ST] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_3ST_DIG] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_6ST_DIG] = { - .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_6stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), - .channel_mode = alc861vd_6stack_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_LENOVO] = { - .mixers = { alc861vd_lenovo_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs, - alc861vd_eapd_verbs, - alc861vd_lenovo_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - .unsol_event = alc861vd_lenovo_unsol_event, - .setup = alc861vd_lenovo_setup, - .init_hook = alc861vd_lenovo_init_hook, - }, - [ALC861VD_DALLAS] = { - .mixers = { alc861vd_dallas_mixer }, - .init_verbs = { alc861vd_dallas_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_dallas_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc861vd_dallas_setup, - .init_hook = alc_hp_automute, - }, - [ALC861VD_HP] = { - .mixers = { alc861vd_hp_mixer }, - .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_hp_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc861vd_dallas_setup, - .init_hook = alc_hp_automute, - }, - [ALC660VD_ASUS_V1S] = { - .mixers = { alc861vd_lenovo_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs, - alc861vd_eapd_verbs, - alc861vd_lenovo_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - .unsol_event = alc861vd_lenovo_unsol_event, - .setup = alc861vd_lenovo_setup, - .init_hook = alc861vd_lenovo_init_hook, - }, +/* set PCBEEP vol = 0, mute connections */ +static const struct hda_verb alc268_beep_init_verbs[] = { + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } }; /* * BIOS auto configuration */ -#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) -#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) - -/* add playback controls from the parsed DAC table */ -/* Based on ALC880 version. But ALC861VD has separate, - * different NIDs for mute/unmute switch and volume control */ -static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid_v, nid_s; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - if (!spec->multiout.dac_nids[i]) - continue; - nid_v = alc861vd_idx_to_mixer_vol( - alc880_dac_to_idx( - spec->multiout.dac_nids[i])); - nid_s = alc861vd_idx_to_mixer_switch( - alc880_dac_to_idx( - spec->multiout.dac_nids[i])); - - name = alc_get_line_out_pfx(spec, i, true, &index); - if (!name) { - /* Center/LFE */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "Center", - HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "LFE", - HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "Center", - HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, - HDA_INPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "LFE", - HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, - HDA_INPUT)); - if (err < 0) - return err; - } else { - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, index, - HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, index, - HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, - HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -/* Based on ALC880 version. But ALC861VD has separate, - * different NIDs for mute/unmute switch and volume control */ -static int alc861vd_auto_create_extra_out(struct alc_spec *spec, - hda_nid_t pin, const char *pfx) -{ - hda_nid_t nid_v, nid_s; - int err; - - if (!pin) - return 0; - - if (alc880_is_fixed_pin(pin)) { - nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid_v; - else - spec->multiout.extra_out_nid[0] = nid_v; - /* control HP volume/switch on the output mixer amp */ - nid_v = alc861vd_idx_to_mixer_vol( - alc880_fixed_pin_idx(pin)); - nid_s = alc861vd_idx_to_mixer_switch( - alc880_fixed_pin_idx(pin)); - - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, - HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } else if (alc880_is_multi_pin(pin)) { - /* set manual connection */ - /* we have only a switch on HP-out PIN */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -/* parse the BIOS configuration and set up the alc_spec - * return 1 if successful, 0 if the proper config is not found, - * or a negative error code - * Based on ALC880 version - had to change it to override - * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ -static int alc861vd_parse_auto_config(struct hda_codec *codec) +static int alc268_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int err; - static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc861vd_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ + static const hda_nid_t alc268_ignore[] = { 0 }; - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc861vd_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc268_ignore); if (err < 0) return err; - err = alc861vd_auto_create_extra_out(spec, - spec->autocfg.hp_pins[0], - "Headphone"); + 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 = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); if (err < 0) return err; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; + spec->multiout.max_channels = 2; + dig_only: + /* digital only support output */ alc_auto_parse_digital(codec); - if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); + if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { + add_mixer(spec, alc268_beep_mixer); + add_verb(spec, alc268_beep_init_verbs); + } err = alc_auto_add_mic_boost(codec); if (err < 0) return err; + alc_remove_invalid_adc_nids(codec); + + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); + return 1; } -/* additional initialization for auto-configuration model */ -static void alc861vd_auto_init(struct hda_codec *codec) +/* init callback for auto-configuration model -- overriding the default init */ +static void alc268_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); + alc268_auto_init_multi_out(codec); + alc268_auto_init_hp_out(codec); + alc268_auto_init_mono_speaker_out(codec); alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec); @@ -16650,32 +4763,17 @@ static void alc861vd_auto_init(struct hda_codec *codec) alc_inithook(codec); } -enum { - ALC660VD_FIX_ASUS_GPIO1 -}; - -/* reset GPIO1 */ -static const struct alc_fixup alc861vd_fixups[] = { - [ALC660VD_FIX_ASUS_GPIO1] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - { } - } - }, -}; - -static const struct snd_pci_quirk alc861vd_fixup_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), - {} -}; +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc268_quirks.c" +#endif -static int patch_alc861vd(struct hda_codec *codec) +static int patch_alc268(struct hda_codec *codec) { struct alc_spec *spec; - int err, board_config; + int board_config; + int i, has_beep, err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -16683,1667 +4781,941 @@ static int patch_alc861vd(struct hda_codec *codec) codec->spec = spec; - spec->mixer_nid = 0x0b; + /* ALC268 has no aa-loopback mixer */ - board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST, - alc861vd_models, - alc861vd_cfg_tbl); + board_config = alc_board_config(codec, ALC268_MODEL_LAST, + alc268_models, alc268_cfg_tbl); - if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { + if (board_config < 0) + board_config = alc_board_codec_sid_config(codec, + ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl); + + if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC861VD_AUTO; - } - - if (board_config == ALC861VD_AUTO) { - alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC861VD_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ - err = alc861vd_parse_auto_config(codec); + err = alc268_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); - board_config = ALC861VD_3ST; + board_config = ALC268_3ST; } +#endif } - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) { - alc_free(codec); - return err; - } + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc268_presets[board_config]); - if (board_config != ALC861VD_AUTO) - setup_preset(codec, &alc861vd_presets[board_config]); + has_beep = 0; + for (i = 0; i < spec->num_mixers; i++) { + if (spec->mixers[i] == alc268_beep_mixer) { + has_beep = 1; + break; + } + } - if (codec->vendor_id == 0x10ec0660) { - /* always turn on EAPD */ - add_verb(spec, alc660vd_eapd_verbs); + if (has_beep) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } + if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) + /* override the amp caps for beep generator */ + snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); } - if (!spec->adc_nids) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (!spec->cap_mixer && !spec->no_analog) + set_capture_mixer(codec); spec->vmaster_nid = 0x02; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - - if (board_config == ALC861VD_AUTO) - spec->init_hook = alc861vd_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc268_auto_init; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc861vd_loopbacks; -#endif + + alc_init_jacks(codec); return 0; } /* - * ALC662 support - * - * ALC662 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). + * ALC269 */ -#define ALC662_DIGOUT_NID 0x06 -#define ALC662_DIGIN_NID 0x0a - -static const hda_nid_t alc662_dac_nids[3] = { - /* front, rear, clfe */ - 0x02, 0x03, 0x04 -}; - -static const hda_nid_t alc272_dac_nids[2] = { - 0x02, 0x03 -}; - -static const hda_nid_t alc662_adc_nids[2] = { - /* ADC1-2 */ - 0x09, 0x08 -}; - -static const hda_nid_t alc272_adc_nids[1] = { - /* ADC1-2 */ - 0x08, -}; - -static const hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 }; -static const hda_nid_t alc272_capsrc_nids[1] = { 0x23 }; +#define alc269_auto_create_multi_out_ctls \ + alc268_auto_create_multi_out_ctls +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc269_loopbacks alc880_loopbacks +#endif -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ -static const struct hda_input_mux alc662_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, +static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ + /* NID is set in alc_build_pcms */ + .ops = { + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup }, }; -static const struct hda_input_mux alc662_lenovo_101e_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Line", 0x2 }, - }, +static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ + /* NID is set in alc_build_pcms */ }; -static const struct hda_input_mux alc663_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int alc269_mic2_for_mute_led(struct hda_codec *codec) +{ + switch (codec->subsystem_id) { + case 0x103c1586: + return 1; + } + return 0; +} -#if 0 /* set to 1 for testing other input sources below */ -static const struct hda_input_mux alc272_nc10_capture_source = { - .num_items = 16, - .items = { - { "Autoselect Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "In-0x02", 0x2 }, - { "In-0x03", 0x3 }, - { "In-0x04", 0x4 }, - { "In-0x05", 0x5 }, - { "In-0x06", 0x6 }, - { "In-0x07", 0x7 }, - { "In-0x08", 0x8 }, - { "In-0x09", 0x9 }, - { "In-0x0a", 0x0a }, - { "In-0x0b", 0x0b }, - { "In-0x0c", 0x0c }, - { "In-0x0d", 0x0d }, - { "In-0x0e", 0x0e }, - { "In-0x0f", 0x0f }, - }, -}; -#endif +static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) +{ + /* update mute-LED according to the speaker mute state */ + if (nid == 0x01 || nid == 0x14) { + int pinval; + if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) & + HDA_AMP_MUTE) + pinval = 0x24; + else + pinval = 0x20; + /* mic2 vref pin is used for mute LED control */ + snd_hda_codec_update_cache(codec, 0x19, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinval); + } + return alc_check_power_status(codec, nid); +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ -/* - * 2ch mode - */ -static const struct hda_channel_mode alc662_3ST_2ch_modes[1] = { - { 2, NULL } +/* different alc269-variants */ +enum { + ALC269_TYPE_ALC269VA, + ALC269_TYPE_ALC269VB, + ALC269_TYPE_ALC269VC, }; /* - * 2ch mode + * BIOS auto configuration */ -static const struct hda_verb alc662_3ST_ch2_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; +static int alc269_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; -/* - * 6ch mode - */ -static const struct hda_verb alc662_3ST_ch6_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc269_ignore); + if (err < 0) + return err; -static const struct hda_channel_mode alc662_3ST_6ch_modes[2] = { - { 2, alc662_3ST_ch2_init }, - { 6, alc662_3ST_ch6_init }, -}; + err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); + if (err < 0) + return err; -/* - * 2ch mode - */ -static const struct hda_verb alc662_sixstack_ch6_init[] = { - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; -/* - * 6ch mode - */ -static const struct hda_verb alc662_sixstack_ch8_init[] = { - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; + alc_auto_parse_digital(codec); -static const struct hda_channel_mode alc662_5stack_modes[2] = { - { 2, alc662_sixstack_ch6_init }, - { 6, alc662_sixstack_ch8_init }, -}; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ + alc_remove_invalid_adc_nids(codec); + + if (spec->codec_variant != ALC269_TYPE_ALC269VA) + alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); + else + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; -static const struct snd_kcontrol_new alc662_base_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - /*Input mixer control */ - HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), - { } /* end */ -}; + if (!spec->cap_mixer && !spec->no_analog) + set_capture_mixer(codec); -static const struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; + return 1; +} -static const struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - 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("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; +#define alc269_auto_init_multi_out alc268_auto_init_multi_out +#define alc269_auto_init_hp_out alc268_auto_init_hp_out -static const struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 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, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; -static const struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, +/* init callback for auto-configuration model -- overriding the default init */ +static void alc269_auto_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc269_auto_init_multi_out(codec); + alc269_auto_init_hp_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); +} - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) +{ + int val = alc_read_coef_idx(codec, 0x04); + if (power_up) + val |= 1 << 11; + else + val &= ~(1 << 11); + alc_write_coef_idx(codec, 0x04, val); +} - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; +static void alc269_shutup(struct hda_codec *codec) +{ + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) + alc269_toggle_power_output(codec, 0); + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { + alc269_toggle_power_output(codec, 0); + msleep(150); + } +} -static const struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = { - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT), - 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), - { } /* end */ -}; +#ifdef SND_HDA_NEEDS_RESUME +static int alc269_resume(struct hda_codec *codec) +{ + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { + alc269_toggle_power_output(codec, 0); + msleep(150); + } -static const struct hda_bind_ctls alc663_asus_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), - 0 - }, -}; + codec->patch_ops.init(codec); -static const struct hda_bind_ctls alc663_asus_one_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - 0 - }, -}; + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) { + alc269_toggle_power_output(codec, 1); + msleep(200); + } -static const struct snd_kcontrol_new alc663_m51va_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) + alc269_toggle_power_output(codec, 1); -static const struct hda_bind_ctls alc663_asus_tree_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - 0 - }, -}; + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + hda_call_check_power_status(codec, 0x01); + return 0; +} +#endif /* SND_HDA_NEEDS_RESUME */ -static const struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +static void alc269_fixup_hweq(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + int coef; - { } /* end */ -}; + if (action != ALC_FIXUP_ACT_INIT) + return; + coef = alc_read_coef_idx(codec, 0x1e); + alc_write_coef_idx(codec, 0x1e, coef | 0x80); +} -static const struct hda_bind_ctls alc663_asus_four_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0 - }, -}; +static void alc271_fixup_dmic(struct hda_codec *codec, + const struct alc_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; -static const struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; + if (strcmp(codec->chip_name, "ALC271X")) + return; + cfg = snd_hda_codec_get_pincfg(codec, 0x12); + if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) + snd_hda_sequence_write(codec, verbs); +} -static const struct snd_kcontrol_new alc662_1bjd_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ +enum { + ALC269_FIXUP_SONY_VAIO, + ALC275_FIXUP_SONY_VAIO_GPIO2, + ALC269_FIXUP_DELL_M101Z, + ALC269_FIXUP_SKU_IGNORE, + ALC269_FIXUP_ASUS_G73JW, + ALC269_FIXUP_LENOVO_EAPD, + ALC275_FIXUP_SONY_HWEQ, + ALC271_FIXUP_DMIC, }; -static const struct hda_bind_ctls alc663_asus_two_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT), - 0 +static const struct alc_fixup alc269_fixups[] = { + [ALC269_FIXUP_SONY_VAIO] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, + {} + } }, -}; - -static const struct hda_bind_ctls alc663_asus_two_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT), - 0 + [ALC275_FIXUP_SONY_VAIO_GPIO2] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_SONY_VAIO + }, + [ALC269_FIXUP_DELL_M101Z] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 13}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, + {} + } + }, + [ALC269_FIXUP_SKU_IGNORE] = { + .type = ALC_FIXUP_SKU, + .v.sku = ALC_FIXUP_SKU_IGNORE, + }, + [ALC269_FIXUP_ASUS_G73JW] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x17, 0x99130111 }, /* subwoofer */ + { } + } + }, + [ALC269_FIXUP_LENOVO_EAPD] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, + [ALC275_FIXUP_SONY_HWEQ] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_hweq, + .chained = true, + .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 + }, + [ALC271_FIXUP_DMIC] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc271_fixup_dmic, }, }; -static const struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", - &alc663_asus_two_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc663_g71v_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ +static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), + SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), + SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), + SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), + SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), + SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), + SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), + {} }; -static const struct snd_kcontrol_new alc663_g50v_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - { } /* end */ -}; -static const struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - 0 - }, -}; +static int alc269_fill_coef(struct hda_codec *codec) +{ + int val; -static const struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), - 0 - }, -}; + if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) { + alc_write_coef_idx(codec, 0xf, 0x960b); + alc_write_coef_idx(codec, 0xe, 0x8817); + } -static const struct snd_kcontrol_new alc663_mode7_mixer[] = { - HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), - HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), - HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) { + alc_write_coef_idx(codec, 0xf, 0x960b); + alc_write_coef_idx(codec, 0xe, 0x8814); + } -static const struct snd_kcontrol_new alc663_mode8_mixer[] = { - HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), - HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), - HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) { + val = alc_read_coef_idx(codec, 0x04); + /* Power up output pin */ + alc_write_coef_idx(codec, 0x04, val | (1<<11)); + } + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { + val = alc_read_coef_idx(codec, 0xd); + if ((val & 0x0c00) >> 10 != 0x1) { + /* Capless ramp up clock control */ + alc_write_coef_idx(codec, 0xd, val | (1<<10)); + } + val = alc_read_coef_idx(codec, 0x17); + if ((val & 0x01c0) >> 6 != 0x4) { + /* Class D power on reset */ + alc_write_coef_idx(codec, 0x17, val | (1<<7)); + } + } -static const struct snd_kcontrol_new alc662_chmode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; + val = alc_read_coef_idx(codec, 0xd); /* Class D */ + alc_write_coef_idx(codec, 0xd, val | (1<<14)); -static const struct hda_verb alc662_init_verbs[] = { - /* ADC: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {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)}, - - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Rear Pin: output 1 (0x0d) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line-2 In: Headphone output (output 0 - 0x0c) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + val = alc_read_coef_idx(codec, 0x4); /* HP */ + alc_write_coef_idx(codec, 0x4, val | (1<<11)); - { } -}; + return 0; +} -static const struct hda_verb alc662_eapd_init_verbs[] = { - /* always trun on EAPD */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc269_quirks.c" +#endif -static const struct hda_verb alc662_sue_init_verbs[] = { - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, - {} -}; +static int patch_alc269(struct hda_codec *codec) +{ + struct alc_spec *spec; + int board_config, coef; + int err; -static const struct hda_verb alc662_eeepc_sue_init_verbs[] = { - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; -/* Set Unsolicited Event*/ -static const struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + codec->spec = spec; -static const struct hda_verb alc663_m51va_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + spec->mixer_nid = 0x0b; -static const struct hda_verb alc663_21jd_amic_init_verbs[] = { - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + alc_auto_parse_customize_define(codec); -static const struct hda_verb alc662_1bjd_amic_init_verbs[] = { - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + if (codec->vendor_id == 0x10ec0269) { + spec->codec_variant = ALC269_TYPE_ALC269VA; + coef = alc_read_coef_idx(codec, 0); + if ((coef & 0x00f0) == 0x0010) { + if (codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) { + alc_codec_rename(codec, "ALC271X"); + } else if ((coef & 0xf000) == 0x2000) { + alc_codec_rename(codec, "ALC259"); + } else if ((coef & 0xf000) == 0x3000) { + alc_codec_rename(codec, "ALC258"); + } else if ((coef & 0xfff0) == 0x3010) { + alc_codec_rename(codec, "ALC277"); + } else { + alc_codec_rename(codec, "ALC269VB"); + } + spec->codec_variant = ALC269_TYPE_ALC269VB; + } else if ((coef & 0x00f0) == 0x0020) { + if (coef == 0xa023) + alc_codec_rename(codec, "ALC259"); + else if (coef == 0x6023) + alc_codec_rename(codec, "ALC281X"); + else if (codec->bus->pci->subsystem_vendor == 0x17aa && + codec->bus->pci->subsystem_device == 0x21f3) + alc_codec_rename(codec, "ALC3202"); + else + alc_codec_rename(codec, "ALC269VC"); + spec->codec_variant = ALC269_TYPE_ALC269VC; + } else + alc_fix_pll_init(codec, 0x20, 0x04, 15); + alc269_fill_coef(codec); + } -static const struct hda_verb alc663_15jd_amic_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + board_config = alc_board_config(codec, ALC269_MODEL_LAST, + alc269_models, alc269_cfg_tbl); -static const struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = { - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC_MODEL_AUTO; + } -static const struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = { - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + if (board_config == ALC_MODEL_AUTO) { + alc_pick_fixup(codec, NULL, alc269_fixup_tbl, alc269_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + } -static const struct hda_verb alc663_g71v_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ - /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */ + if (board_config == ALC_MODEL_AUTO) { + /* automatic parse from the BIOS config */ + err = alc269_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC269_BASIC; + } +#endif + } - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ + if (has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } + } - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, - {} -}; + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc269_presets[board_config]); -static const struct hda_verb alc663_g50v_init_verbs[] = { - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ +#if 0 + if (board_config == ALC269_QUANTA_FL1) { + /* Due to a hardware problem on Lenovo Ideadpad, we need to + * fix the sample rate of analog I/O to 44.1kHz + */ + spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; + } +#endif - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + if (!spec->adc_nids) { /* wasn't filled automatically? use default */ + alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); + alc_remove_invalid_adc_nids(codec); + } -static const struct hda_verb alc662_ecs_init_verbs[] = { - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + if (!spec->cap_mixer) + set_capture_mixer(codec); + if (has_cdefine_beep(codec)) + set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); -static const struct hda_verb alc272_dell_zm1_init_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); -static const struct hda_verb alc272_dell_init_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + spec->vmaster_nid = 0x02; -static const struct hda_verb alc663_mode7_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + codec->patch_ops = alc_patch_ops; +#ifdef SND_HDA_NEEDS_RESUME + codec->patch_ops.resume = alc269_resume; +#endif + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc269_auto_init; + spec->shutup = alc269_shutup; -static const struct hda_verb alc663_mode8_init_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; + alc_init_jacks(codec); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc269_loopbacks; + if (alc269_mic2_for_mute_led(codec)) + codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps; +#endif -static const struct snd_kcontrol_new alc662_auto_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { } /* end */ -}; + return 0; +} -static const struct snd_kcontrol_new alc272_auto_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { } /* end */ -}; +/* + * ALC861 + */ -static void alc662_lenovo_101e_setup(struct hda_codec *codec) +static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; + hda_nid_t mix, srcs[5]; + int i, num; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.line_out_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->detect_line = 1; - spec->automute_lines = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; + if (snd_hda_get_connections(codec, pin, &mix, 1) != 1) + return 0; + num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); + if (num < 0) + return 0; + for (i = 0; i < num; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, srcs[i])); + if (type != AC_WID_AUD_OUT) + continue; + if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids, + spec->multiout.num_dacs)) + return srcs[i]; + } + return 0; } -static void alc662_eeepc_setup(struct hda_codec *codec) +/* fill in the dac_nids table from the parsed pin configuration */ +static int alc861_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + hda_nid_t nid, dac; - alc262_hippo1_setup(codec); - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + dac = alc861_look_for_dac(codec, nid); + if (!dac) + continue; + spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + } + return 0; } -static void alc662_eeepc_ep20_setup(struct hda_codec *codec) +static int __alc861_create_out_sw(struct hda_codec *codec, const char *pfx, + hda_nid_t nid, int idx, unsigned int chs) { - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; + return __add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, idx, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -static void alc663_m51va_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; -} +#define alc861_create_out_sw(codec, pfx, nid, chs) \ + __alc861_create_out_sw(codec, pfx, nid, 0, chs) -/* ***************** Mode1 ******************************/ -static void alc663_mode1_setup(struct hda_codec *codec) +/* add playback controls from the parsed DAC table */ +static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; + hda_nid_t nid; + int i, err, noutputs; + + noutputs = cfg->line_outs; + if (spec->multi_ios > 0) + noutputs += spec->multi_ios; + + for (i = 0; i < noutputs; i++) { + const char *name; + int index; + nid = spec->multiout.dac_nids[i]; + if (!nid) + continue; + name = alc_get_line_out_pfx(spec, i, true, &index); + if (!name) { + /* Center/LFE */ + err = alc861_create_out_sw(codec, "Center", nid, 1); + if (err < 0) + return err; + err = alc861_create_out_sw(codec, "LFE", nid, 2); + if (err < 0) + return err; + } else { + err = __alc861_create_out_sw(codec, name, nid, index, 3); + if (err < 0) + return err; + } + } + return 0; } -/* ***************** Mode2 ******************************/ -static void alc662_mode2_setup(struct hda_codec *codec) +static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; + int err; + hda_nid_t nid; + + if (!pin) + return 0; + + if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) { + nid = alc861_look_for_dac(codec, pin); + if (nid) { + err = alc861_create_out_sw(codec, "Headphone", nid, 3); + if (err < 0) + return err; + spec->multiout.hp_nid = nid; + } + } + return 0; } -/* ***************** Mode3 ******************************/ -static void alc663_mode3_setup(struct hda_codec *codec) +static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, + int pin_type, hda_nid_t dac) { - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; -} + hda_nid_t mix, srcs[5]; + int i, num; -/* ***************** Mode4 ******************************/ -static void alc663_mode4_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute_mixer_nid[1] = 0x0e; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_type); + snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + if (snd_hda_get_connections(codec, nid, &mix, 1) != 1) + return; + num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); + if (num < 0) + return; + for (i = 0; i < num; i++) { + unsigned int mute; + if (srcs[i] == dac || srcs[i] == 0x15) + mute = AMP_IN_UNMUTE(i); + else + mute = AMP_IN_MUTE(i); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + mute); + } } -/* ***************** Mode5 ******************************/ -static void alc663_mode5_setup(struct hda_codec *codec) +static void alc861_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute_mixer_nid[1] = 0x0e; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; -} + int i; -/* ***************** Mode6 ******************************/ -static void alc663_mode6_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; + for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + int pin_type = get_pin_type(spec->autocfg.line_out_type); + if (nid) + alc861_auto_set_output_and_unmute(codec, nid, pin_type, + spec->multiout.dac_nids[i]); + } } -/* ***************** Mode7 ******************************/ -static void alc663_mode7_setup(struct hda_codec *codec) +static void alc861_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x19; - spec->auto_mic = 1; -} -/* ***************** Mode8 ******************************/ -static void alc663_mode8_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.hp_pins[1] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; + if (spec->autocfg.hp_outs) + alc861_auto_set_output_and_unmute(codec, + spec->autocfg.hp_pins[0], + PIN_HP, + spec->multiout.hp_nid); + if (spec->autocfg.speaker_outs) + alc861_auto_set_output_and_unmute(codec, + spec->autocfg.speaker_pins[0], + PIN_OUT, + spec->multiout.dac_nids[0]); } -static void alc663_g71v_setup(struct hda_codec *codec) +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code + */ +static int alc861_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.line_out_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; - spec->detect_line = 1; - spec->automute_lines = 1; - spec->ext_mic_pin = 0x18; - spec->int_mic_pin = 0x12; - spec->auto_mic = 1; -} - -#define alc663_g50v_setup alc663_m51va_setup - -static const struct snd_kcontrol_new alc662_ecs_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - - HDA_CODEC_VOLUME("Mic/LineIn Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT), - - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc272_nc10_mixer[] = { - /* Master Playback automatically created from Speaker and Headphone */ - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - 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 Volume", 0x18, 0, HDA_INPUT), - - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc662_loopbacks alc880_loopbacks -#endif + int err; + static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc861_ignore); + if (err < 0) + return err; + if (!spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ -/* - * configuration and preset - */ -static const char * const alc662_models[ALC662_MODEL_LAST] = { - [ALC662_3ST_2ch_DIG] = "3stack-dig", - [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig", - [ALC662_3ST_6ch] = "3stack-6ch", - [ALC662_5ST_DIG] = "5stack-dig", - [ALC662_LENOVO_101E] = "lenovo-101e", - [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", - [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", - [ALC662_ECS] = "ecs", - [ALC663_ASUS_M51VA] = "m51va", - [ALC663_ASUS_G71V] = "g71v", - [ALC663_ASUS_H13] = "h13", - [ALC663_ASUS_G50V] = "g50v", - [ALC663_ASUS_MODE1] = "asus-mode1", - [ALC662_ASUS_MODE2] = "asus-mode2", - [ALC663_ASUS_MODE3] = "asus-mode3", - [ALC663_ASUS_MODE4] = "asus-mode4", - [ALC663_ASUS_MODE5] = "asus-mode5", - [ALC663_ASUS_MODE6] = "asus-mode6", - [ALC663_ASUS_MODE7] = "asus-mode7", - [ALC663_ASUS_MODE8] = "asus-mode8", - [ALC272_DELL] = "dell", - [ALC272_DELL_ZM1] = "dell-zm1", - [ALC272_SAMSUNG_NC10] = "samsung-nc10", - [ALC662_AUTO] = "auto", -}; + err = alc861_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); + if (err < 0) + return err; -static const struct snd_pci_quirk alc662_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), - SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL), - SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1), - SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), - SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), - /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/ - SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), - /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/ - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4), - SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), - SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), - SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), - SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", - ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), - SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", - ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), - SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), - SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0", - ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", - ALC663_ASUS_H13), - SND_PCI_QUIRK(0x1991, 0x5628, "Ordissimo EVE", ALC662_LENOVO_101E), - {} -}; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; -static const struct alc_config_preset alc662_presets[] = { - [ALC662_3ST_2ch_DIG] = { - .mixers = { alc662_3ST_2ch_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .dig_in_nid = ALC662_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_capture_source, - }, - [ALC662_3ST_6ch_DIG] = { - .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .dig_in_nid = ALC662_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc662_capture_source, - }, - [ALC662_3ST_6ch] = { - .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc662_capture_source, - }, - [ALC662_5ST_DIG] = { - .mixers = { alc662_base_mixer, alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .dig_in_nid = ALC662_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), - .channel_mode = alc662_5stack_modes, - .input_mux = &alc662_capture_source, - }, - [ALC662_LENOVO_101E] = { - .mixers = { alc662_lenovo_101e_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_sue_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_lenovo_101e_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_lenovo_101e_setup, - .init_hook = alc_inithook, - }, - [ALC662_ASUS_EEEPC_P701] = { - .mixers = { alc662_eeepc_p701_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_eeepc_sue_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_eeepc_setup, - .init_hook = alc_inithook, - }, - [ALC662_ASUS_EEEPC_EP20] = { - .mixers = { alc662_eeepc_ep20_mixer, - alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_eeepc_ep20_sue_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .input_mux = &alc662_lenovo_101e_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_eeepc_ep20_setup, - .init_hook = alc_inithook, - }, - [ALC662_ECS] = { - .mixers = { alc662_ecs_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_ecs_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_eeepc_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_M51VA] = { - .mixers = { alc663_m51va_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_m51va_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_m51va_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_G71V] = { - .mixers = { alc663_g71v_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_g71v_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_g71v_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_H13] = { - .mixers = { alc663_m51va_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_m51va_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .setup = alc663_m51va_setup, - .unsol_event = alc_sku_unsol_event, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_G50V] = { - .mixers = { alc663_g50v_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_g50v_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .input_mux = &alc663_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_g50v_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE1] = { - .mixers = { alc663_m51va_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_21jd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode1_setup, - .init_hook = alc_inithook, - }, - [ALC662_ASUS_MODE2] = { - .mixers = { alc662_1bjd_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_1bjd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_mode2_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE3] = { - .mixers = { alc663_two_hp_m1_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_two_hp_amic_m1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode3_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE4] = { - .mixers = { alc663_asus_21jd_clfe_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_21jd_amic_init_verbs}, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode4_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE5] = { - .mixers = { alc663_asus_15jd_clfe_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_15jd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode5_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE6] = { - .mixers = { alc663_two_hp_m2_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_two_hp_amic_m2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode6_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE7] = { - .mixers = { alc663_mode7_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_mode7_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode7_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE8] = { - .mixers = { alc663_mode8_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_mode8_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode8_setup, - .init_hook = alc_inithook, - }, - [ALC272_DELL] = { - .mixers = { alc663_m51va_mixer }, - .cap_mixer = alc272_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc272_dell_init_verbs }, - .num_dacs = ARRAY_SIZE(alc272_dac_nids), - .dac_nids = alc272_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .adc_nids = alc272_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc272_adc_nids), - .capsrc_nids = alc272_capsrc_nids, - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_m51va_setup, - .init_hook = alc_inithook, - }, - [ALC272_DELL_ZM1] = { - .mixers = { alc663_m51va_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc272_dell_zm1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc272_dac_nids), - .dac_nids = alc272_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .adc_nids = alc662_adc_nids, - .num_adc_nids = 1, - .capsrc_nids = alc662_capsrc_nids, - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_m51va_setup, - .init_hook = alc_inithook, - }, - [ALC272_SAMSUNG_NC10] = { - .mixers = { alc272_nc10_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_21jd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc272_dac_nids), - .dac_nids = alc272_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - /*.input_mux = &alc272_nc10_capture_source,*/ - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode4_setup, - .init_hook = alc_inithook, - }, -}; + alc_auto_parse_digital(codec); + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); -/* - * BIOS auto configuration - */ + alc_remove_invalid_adc_nids(codec); -/* convert from MIX nid to DAC */ -static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) -{ - hda_nid_t list[5]; - int i, num; + alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); + alc_auto_check_switches(codec); - num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); - for (i = 0; i < num; i++) { - if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) - return list[i]; - } - return 0; -} + set_capture_mixer(codec); -/* go down to the selector widget before the mixer */ -static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t srcs[5]; - int num = snd_hda_get_connections(codec, pin, srcs, - ARRAY_SIZE(srcs)); - if (num != 1 || - get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) - return pin; - return srcs[0]; + return 1; } -/* get MIX nid connected to the given pin targeted to DAC */ -static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +/* additional initialization for auto-configuration model */ +static void alc861_auto_init(struct hda_codec *codec) { - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) - return mix[i]; - } - return 0; + struct alc_spec *spec = codec->spec; + alc861_auto_init_multi_out(codec); + alc861_auto_init_hp_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); } -/* select the connection from pin to DAC if needed */ -static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static const struct hda_amp_list alc861_loopbacks[] = { + { 0x15, HDA_INPUT, 0 }, + { 0x15, HDA_INPUT, 1 }, + { 0x15, HDA_INPUT, 2 }, + { 0x15, HDA_INPUT, 3 }, + { } /* end */ +}; +#endif - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - if (num < 2) - return 0; - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, i); - return 0; - } - } - return 0; -} -/* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t srcs[5]; - int i, num; +/* Pin config fixes */ +enum { + PINFIX_FSC_AMILO_PI1505, +}; - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (!nid) - continue; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - spec->multiout.num_dacs)) - continue; - if (spec->multiout.hp_nid == nid) - continue; - if (found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - continue; - return nid; - } - return 0; -} +static const struct alc_fixup alc861_fixups[] = { + [PINFIX_FSC_AMILO_PI1505] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x0b, 0x0221101f }, /* HP */ + { 0x0f, 0x90170310 }, /* speaker */ + { } + } + }, +}; -static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t sel = alc_go_down_to_selector(codec, pin); - if (snd_hda_get_conn_list(codec, sel, NULL) == 1) - return alc_auto_look_for_dac(codec, pin); - return 0; -} +static const struct snd_pci_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), + {} +}; -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc_auto_fill_dac_nids(struct hda_codec *codec) +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc861_quirks.c" +#endif + +static int patch_alc861(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - bool redone = false; - int i; + struct alc_spec *spec; + int board_config; + int err; - again: - spec->multiout.num_dacs = 0; - spec->multiout.hp_nid = 0; - spec->multiout.extra_out_nid[0] = 0; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); - spec->multiout.dac_nids = spec->private_dac_nids; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; - /* fill hard-wired DACs first */ - if (!redone) { - for (i = 0; i < cfg->line_outs; i++) - spec->private_dac_nids[i] = - get_dac_if_single(codec, cfg->line_out_pins[i]); - if (cfg->hp_outs) - spec->multiout.hp_nid = - get_dac_if_single(codec, cfg->hp_pins[0]); - if (cfg->speaker_outs) - spec->multiout.extra_out_nid[0] = - get_dac_if_single(codec, cfg->speaker_pins[0]); + codec->spec = spec; + + spec->mixer_nid = 0x15; + + board_config = alc_board_config(codec, ALC861_MODEL_LAST, + alc861_models, alc861_cfg_tbl); + + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC_MODEL_AUTO; } - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t pin = cfg->line_out_pins[i]; - if (spec->private_dac_nids[i]) - continue; - spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin); - if (!spec->private_dac_nids[i] && !redone) { - /* if we can't find primary DACs, re-probe without - * checking the hard-wired DACs - */ - redone = true; - goto again; + if (board_config == ALC_MODEL_AUTO) { + alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + } + + if (board_config == ALC_MODEL_AUTO) { + /* automatic parse from the BIOS config */ + err = alc861_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC861_3ST_DIG; } +#endif } - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + err = snd_hda_attach_beep_device(codec, 0x23); + if (err < 0) { + alc_free(codec); + return err; } - if (cfg->hp_outs && !spec->multiout.hp_nid) - spec->multiout.hp_nid = - alc_auto_look_for_dac(codec, cfg->hp_pins[0]); - if (cfg->speaker_outs && !spec->multiout.extra_out_nid[0]) - spec->multiout.extra_out_nid[0] = - alc_auto_look_for_dac(codec, cfg->speaker_pins[0]); + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc861_presets[board_config]); - return 0; -} + if (!spec->adc_nids) { + alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); + alc_remove_invalid_adc_nids(codec); + } -static int alc_auto_add_vol_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) -{ - return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); -} + if (!spec->cap_mixer) + set_capture_mixer(codec); + set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); -#define alc_auto_add_stereo_vol(codec, pfx, cidx, nid) \ - alc_auto_add_vol_ctl(codec, pfx, cidx, nid, 3) + spec->vmaster_nid = 0x03; -/* create a mute-switch for the given mixer widget; - * if it has multiple sources (e.g. DAC and loopback), create a bind-mute - */ -static int alc_auto_add_sw_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) -{ - int type; - unsigned long val; - if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); - } else { - type = ALC_CTL_BIND_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC_MODEL_AUTO) { + spec->init_hook = alc861_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->power_hook = alc_power_eapd; +#endif } - return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc861_loopbacks; +#endif + + return 0; } -#define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \ - alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3) +/* + * ALC861-VD support + * + * Based on ALC882 + * + * In addition, an independent DAC + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc861vd_loopbacks alc880_loopbacks +#endif + +/* + * BIOS auto configuration + */ +#define alc861vd_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) +#define alc861vd_fixed_pin_idx(nid) ((nid) - 0x14) +#define alc861vd_is_multi_pin(nid) ((nid) >= 0x18) +#define alc861vd_multi_pin_idx(nid) ((nid) - 0x18) +#define alc861vd_idx_to_dac(nid) ((nid) + 0x02) +#define alc861vd_dac_to_idx(nid) ((nid) - 0x02) +#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) +#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) /* add playback controls from the parsed DAC table */ -static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, +/* Based on ALC880 version. But ALC861VD has separate, + * different NIDs for mute/unmute switch and volume control */ +static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - struct alc_spec *spec = codec->spec; - hda_nid_t nid, mix, pin; + hda_nid_t nid_v, nid_s; int i, err, noutputs; noutputs = cfg->line_outs; @@ -18353,36 +5725,53 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, for (i = 0; i < noutputs; i++) { const char *name; int index; - nid = spec->multiout.dac_nids[i]; - if (!nid) - continue; - if (i >= cfg->line_outs) - pin = spec->multi_io[i - 1].pin; - else - pin = cfg->line_out_pins[i]; - mix = alc_auto_dac_to_mix(codec, pin, nid); - if (!mix) + if (!spec->multiout.dac_nids[i]) continue; + nid_v = alc861vd_idx_to_mixer_vol( + alc861vd_dac_to_idx( + spec->multiout.dac_nids[i])); + nid_s = alc861vd_idx_to_mixer_switch( + alc861vd_dac_to_idx( + spec->multiout.dac_nids[i])); + name = alc_get_line_out_pfx(spec, i, true, &index); if (!name) { /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, nid, 1); + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", + HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, nid, 2); + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", + HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, mix, 1); + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", + HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, + HDA_INPUT)); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, mix, 2); + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", + HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, + HDA_INPUT)); if (err < 0) return err; } else { - err = alc_auto_add_stereo_vol(codec, name, index, nid); + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + name, index, + HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, name, index, mix); + err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + name, index, + HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, + HDA_INPUT)); if (err < 0) return err; } @@ -18390,288 +5779,260 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, return 0; } -/* add playback controls for speaker and HP outputs */ -static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, const char *pfx) +/* add playback controls for speaker and HP outputs */ +/* Based on ALC880 version. But ALC861VD has separate, + * different NIDs for mute/unmute switch and volume control */ +static int alc861vd_auto_create_extra_out(struct alc_spec *spec, + hda_nid_t pin, const char *pfx) +{ + hda_nid_t nid_v, nid_s; + int err; + + if (!pin) + return 0; + + if (alc861vd_is_fixed_pin(pin)) { + nid_v = alc861vd_idx_to_dac(alc861vd_fixed_pin_idx(pin)); + /* specify the DAC as the extra output */ + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = nid_v; + else + spec->multiout.extra_out_nid[0] = nid_v; + /* control HP volume/switch on the output mixer amp */ + nid_v = alc861vd_idx_to_mixer_vol( + alc861vd_fixed_pin_idx(pin)); + nid_s = alc861vd_idx_to_mixer_switch( + alc861vd_fixed_pin_idx(pin)); + + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, + HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, + HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); + if (err < 0) + return err; + } else if (alc861vd_is_multi_pin(pin)) { + /* set manual connection */ + /* we have only a switch on HP-out PIN */ + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + return 0; +} + +/* parse the BIOS configuration and set up the alc_spec + * return 1 if successful, 0 if the proper config is not found, + * or a negative error code + * Based on ALC880 version - had to change it to override + * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ +static int alc861vd_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t mix; int err; + static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - if (!pin) - return 0; - if (!dac) { - /* the corresponding DAC is already occupied */ - if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) - return 0; /* no way */ - /* create a switch only */ - return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - } + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc861vd_ignore); + if (err < 0) + return err; + if (!spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ - mix = alc_auto_dac_to_mix(codec, pin, dac); - if (!mix) - return 0; - err = alc_auto_add_stereo_vol(codec, pfx, 0, dac); + err = alc_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, pfx, 0, mix); + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc861vd_auto_create_extra_out(spec, + spec->autocfg.speaker_pins[0], + "Speaker"); + if (err < 0) + return err; + err = alc861vd_auto_create_extra_out(spec, + spec->autocfg.hp_pins[0], + "Headphone"); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; - return 0; -} - -static int alc_auto_create_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], - spec->multiout.hp_nid, - "Headphone"); -} - -static int alc_auto_create_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], - spec->multiout.extra_out_nid[0], - "Speaker"); -} - -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - hda_nid_t dac) -{ - int i, num; - hda_nid_t mix = 0; - hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - - alc_set_pin_output(codec, nid, pin_type); - nid = alc_go_down_to_selector(codec, nid); - num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) - continue; - mix = srcs[i]; - break; - } - if (!mix) - return; - - /* need the manual connection? */ - if (num > 1) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); - /* unmute mixer widget inputs */ - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - /* initialize volume */ - if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) - nid = dac; - else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) - nid = mix; - else - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); -} - -static void alc_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - int i; - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} + spec->multiout.max_channels = spec->multiout.num_dacs * 2; -static void alc_auto_init_extra_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; + alc_auto_parse_digital(codec); - pin = spec->autocfg.hp_pins[0]; - if (pin) - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, - spec->multiout.hp_nid); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, - spec->multiout.extra_out_nid[0]); -} + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); -/* - * multi-io helper - */ -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - unsigned int location) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, num_pins = 0; + alc_remove_invalid_adc_nids(codec); - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - hda_nid_t dac; - unsigned int defcfg, caps; - if (cfg->inputs[i].type != type) - continue; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - continue; - if (location && get_defcfg_location(defcfg) != location) - continue; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - continue; - dac = alc_auto_look_for_dac(codec, nid); - if (!dac) - continue; - spec->multi_io[num_pins].pin = nid; - spec->multi_io[num_pins].dac = dac; - num_pins++; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - } - spec->multiout.num_dacs = 1; - if (num_pins < 2) - return 0; - return num_pins; -} + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); + alc_auto_check_switches(codec); -static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); - return 0; + return 1; } -static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* additional initialization for auto-configuration model */ +static void alc861vd_auto_init(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; - return 0; + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); } -static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - - if (!spec->multi_io[idx].ctl_in) - spec->multi_io[idx].ctl_in = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (output) { - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); - } else { - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->multi_io[idx].ctl_in); - } - return 0; -} +enum { + ALC660VD_FIX_ASUS_GPIO1 +}; -static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int i, ch; +/* reset GPIO1 */ +static const struct alc_fixup alc861vd_fixups[] = { + [ALC660VD_FIX_ASUS_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, + { } + } + }, +}; - ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) - return 0; - spec->ext_channel_count = (ch + 1) * 2; - for (i = 0; i < spec->multi_ios; i++) - alc_set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = spec->ext_channel_count; - return 1; -} +static const struct snd_pci_quirk alc861vd_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), + {} +}; -static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_auto_ch_mode_info, - .get = alc_auto_ch_mode_get, - .put = alc_auto_ch_mode_put, +static const struct hda_verb alc660vd_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } }; -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, - int (*fill_dac)(struct hda_codec *)) +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc861vd_quirks.c" +#endif + +static int patch_alc861vd(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int location, defcfg; - int num_pins; + struct alc_spec *spec; + int err, board_config; - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) { - /* use HP as primary out */ - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - if (fill_dac) - fill_dac(codec); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->mixer_nid = 0x0b; + + board_config = alc_board_config(codec, ALC861VD_MODEL_LAST, + alc861vd_models, alc861vd_cfg_tbl); + + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC_MODEL_AUTO; } - if (cfg->line_outs != 1 || - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - return 0; - defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]); - location = get_defcfg_location(defcfg); + if (board_config == ALC_MODEL_AUTO) { + alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + } - num_pins = alc_auto_fill_multi_ios(codec, location); - if (num_pins > 0) { - struct snd_kcontrol_new *knew; + if (board_config == ALC_MODEL_AUTO) { + /* automatic parse from the BIOS config */ + err = alc861vd_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC861VD_3ST; + } +#endif + } - knew = alc_kcontrol_new(spec); - if (!knew) - return -ENOMEM; - *knew = alc_auto_channel_mode_enum; - knew->name = kstrdup("Channel Mode", GFP_KERNEL); - if (!knew->name) - return -ENOMEM; + err = snd_hda_attach_beep_device(codec, 0x23); + if (err < 0) { + alc_free(codec); + return err; + } - spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc861vd_presets[board_config]); + + if (codec->vendor_id == 0x10ec0660) { + /* always turn on EAPD */ + add_verb(spec, alc660vd_eapd_verbs); + } + + if (!spec->adc_nids) { + alc_auto_fill_adc_caps(codec); + alc_rebuild_imux_for_auto_mic(codec); + alc_remove_invalid_adc_nids(codec); } + + set_capture_mixer(codec); + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + spec->vmaster_nid = 0x02; + + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + + codec->patch_ops = alc_patch_ops; + + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc861vd_auto_init; + spec->shutup = alc_eapd_shutup; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc861vd_loopbacks; +#endif + return 0; } +/* + * ALC662 support + * + * ALC662 is almost identical with ALC880 but has cleaner and more flexible + * configuration. Each pin widget can choose any input DACs and a mixer. + * Each ADC is connected from a mixer of all inputs. This makes possible + * 6-channel independent captures. + * + * In addition, an independent DAC for the multi-playback (not used in this + * driver yet). + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc662_loopbacks alc880_loopbacks +#endif + +/* + * BIOS auto configuration + */ + static int alc662_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -18816,6 +6177,12 @@ static const struct alc_model_fixup alc662_fixup_models[] = { }; +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc662_quirks.c" +#endif + static int patch_alc662(struct hda_codec *codec) { struct alc_spec *spec; @@ -18844,16 +6211,15 @@ static int patch_alc662(struct hda_codec *codec) else if (coef == 0x4011) alc_codec_rename(codec, "ALC656"); - board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, - alc662_models, - alc662_cfg_tbl); + board_config = alc_board_config(codec, ALC662_MODEL_LAST, + alc662_models, alc662_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC662_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC662_AUTO) { + if (board_config == ALC_MODEL_AUTO) { alc_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -18862,12 +6228,15 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); board_config = ALC662_3ST_2ch_DIG; } +#endif } if (has_cdefine_beep(codec)) { @@ -18878,7 +6247,7 @@ static int patch_alc662(struct hda_codec *codec) } } - if (board_config != ALC662_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc662_presets[board_config]); if (!spec->adc_nids) { @@ -18910,7 +6279,7 @@ static int patch_alc662(struct hda_codec *codec) alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); codec->patch_ops = alc_patch_ops; - if (board_config == ALC662_AUTO) + if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc662_auto_init; spec->shutup = alc_eapd_shutup; @@ -18953,187 +6322,6 @@ static int patch_alc899(struct hda_codec *codec) /* * ALC680 support */ -#define ALC680_DIGIN_NID ALC880_DIGIN_NID -#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID -#define alc680_modes alc260_modes - -static const hda_nid_t alc680_dac_nids[3] = { - /* Lout1, Lout2, hp */ - 0x02, 0x03, 0x04 -}; - -static const hda_nid_t alc680_adc_nids[3] = { - /* ADC0-2 */ - /* DMIC, MIC, Line-in*/ - 0x07, 0x08, 0x09 -}; - -/* - * Analog capture ADC cgange - */ -static hda_nid_t alc680_get_cur_adc(struct hda_codec *codec) -{ - static hda_nid_t pins[] = {0x18, 0x19}; - static hda_nid_t adcs[] = {0x08, 0x09}; - int i; - - for (i = 0; i < ARRAY_SIZE(pins); i++) { - if (!is_jack_detectable(codec, pins[i])) - continue; - if (snd_hda_jack_detect(codec, pins[i])) - return adcs[i]; - } - return 0x07; -} - -static void alc680_rec_autoswitch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid = alc680_get_cur_adc(codec); - if (spec->cur_adc && nid != spec->cur_adc) { - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = nid; - snd_hda_codec_setup_stream(codec, nid, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } -} - -static int alc680_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; - hda_nid_t nid = alc680_get_cur_adc(codec); - - spec->cur_adc = nid; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); - return 0; -} - -static int alc680_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 const struct hda_pcm_stream alc680_pcm_analog_auto_capture = { - .substreams = 1, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .prepare = alc680_capture_pcm_prepare, - .cleanup = alc680_capture_pcm_cleanup - }, -}; - -static const 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("Internal Mic Boost Volume", 0x12, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct hda_bind_ctls alc680_bind_cap_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc680_bind_cap_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc680_master_capture_mixer[] = { - HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol), - HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch), - { } /* end */ -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc680_init_verbs[] = { - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {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}, - - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc680_base_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x16; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.num_inputs = 2; - spec->autocfg.inputs[0].pin = 0x18; - spec->autocfg.inputs[0].type = AUTO_PIN_MIC; - spec->autocfg.inputs[1].pin = 0x19; - spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc680_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc_hp_automute(codec); - if ((res >> 26) == ALC880_MIC_EVENT) - alc680_rec_autoswitch(codec); -} - -static void alc680_inithook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc680_rec_autoswitch(codec); -} - /* 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) @@ -19300,34 +6488,10 @@ static void alc680_auto_init(struct hda_codec *codec) } /* - * configuration and preset */ -static const char * const alc680_models[ALC680_MODEL_LAST] = { - [ALC680_BASE] = "base", - [ALC680_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc680_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE), - {} -}; - -static const struct alc_config_preset alc680_presets[] = { - [ALC680_BASE] = { - .mixers = { alc680_base_mixer }, - .cap_mixer = alc680_master_capture_mixer, - .init_verbs = { alc680_init_verbs }, - .num_dacs = ARRAY_SIZE(alc680_dac_nids), - .dac_nids = alc680_dac_nids, - .dig_out_nid = ALC680_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc680_modes), - .channel_mode = alc680_modes, - .unsol_event = alc680_unsol_event, - .setup = alc680_base_setup, - .init_hook = alc680_inithook, - - }, -}; +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc680_quirks.c" +#endif static int patch_alc680(struct hda_codec *codec) { @@ -19343,33 +6507,37 @@ static int patch_alc680(struct hda_codec *codec) /* ALC680 has no aa-loopback mixer */ - board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST, - alc680_models, - alc680_cfg_tbl); + board_config = alc_board_config(codec, ALC680_MODEL_LAST, + alc680_models, alc680_cfg_tbl); - if (board_config < 0 || board_config >= ALC680_MODEL_LAST) { + if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC680_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC680_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc680_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + else if (!err) { printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); board_config = ALC680_BASE; } +#endif } - if (board_config != ALC680_AUTO) { + if (board_config != ALC_MODEL_AUTO) { setup_preset(codec, &alc680_presets[board_config]); +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS spec->stream_analog_capture = &alc680_pcm_analog_auto_capture; +#endif } if (!spec->adc_nids) { @@ -19384,7 +6552,7 @@ static int patch_alc680(struct hda_codec *codec) spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; - if (board_config == ALC680_AUTO) + if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc680_auto_init; return 0; -- cgit v1.2.3 From d69607b3c39bb46b7f838f7b683716d4c22ee353 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Fri, 8 Jul 2011 14:02:52 +0800 Subject: ALSA: hda - Fix VIA output-path init for VT2002P/1802/1812 For VT2002P, VT1802 and VT1812 codecs, the original activate_output_path() function can't initialize output and hp path correctly, since mixers connected to output pin widgets are not considered. So modify the activate_output_path() function to satisify this kind of codec. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 104 +++++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 37 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 42d5a91781fc..8f59e0b5d477 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -438,11 +438,62 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, #define have_mute(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) +static bool is_node_in_path(struct nid_path *path, hda_nid_t nid) +{ + int i; + if (!nid) + return false; + for (i = 0; i < path->depth; i++) { + if (path->path[i] == nid) + return true; + } + return false; +} + +/* enable/disable the output-route mixers */ +static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, + hda_nid_t mix_nid, int aa_mix_idx, bool enable) +{ + int i, num, val; + bool hp_path, front_path; + struct via_spec *spec = codec->spec; + + if (!path) + return; + num = snd_hda_get_conn_list(codec, mix_nid, NULL); + hp_path = is_node_in_path(path, spec->hp_dac_nid); + front_path = is_node_in_path(path, spec->multiout.dac_nids[0]); + + for (i = 0; i < num; i++) { + if (i == aa_mix_idx) { + if (hp_path) + val = enable ? AMP_IN_MUTE(i) : + AMP_IN_UNMUTE(i); + else if (front_path) + val = AMP_IN_UNMUTE(i); + else + val = AMP_IN_MUTE(i); + } else { + if (hp_path) + val = enable ? AMP_IN_UNMUTE(i) : + AMP_IN_MUTE(i); + else if (front_path) + val = AMP_IN_MUTE(i); + else + val = AMP_IN_UNMUTE(i); + } + snd_hda_codec_write(codec, mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + /* enable/disable the output-route */ static void activate_output_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool force) { - int i; + int i, val; + struct via_spec *spec = codec->spec; + hda_nid_t aa_mix_nid = spec->aa_mix_nid; for (i = 0; i < path->depth; i++) { hda_nid_t src, dst; int idx = path->idx[i]; @@ -459,10 +510,19 @@ static void activate_output_path(struct hda_codec *codec, struct nid_path *path, && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX) continue; if (have_mute(codec, dst, HDA_INPUT)) { - int val = enable ? AMP_IN_UNMUTE(idx) : - AMP_IN_MUTE(idx); - snd_hda_codec_write(codec, dst, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); + if (dst == aa_mix_nid) { + val = enable ? AMP_IN_UNMUTE(idx) : + AMP_IN_MUTE(idx); + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } else { + idx = get_connection_index(codec, dst, + aa_mix_nid); + if (idx >= 0) { + activate_output_mix(codec, path, + dst, idx, enable); + } + } } if (!force && (src == path->vol_ctl || src == path->mute_ctl)) continue; @@ -493,8 +553,7 @@ static void via_auto_init_output(struct hda_codec *codec, { struct via_spec *spec = codec->spec; unsigned int caps; - hda_nid_t pin, nid, pre_nid; - int i, idx, j, num; + hda_nid_t pin; if (!path->depth) return; @@ -509,39 +568,10 @@ static void via_auto_init_output(struct hda_codec *codec, AMP_OUT_MUTE | val); } - activate_output_path(codec, path, true, force); - /* initialize the AA-path */ if (!spec->aa_mix_nid) return; - for (i = path->depth - 1; i > 0; i--) { - nid = path->path[i]; - pre_nid = path->path[i - 1]; - idx = get_connection_index(codec, nid, spec->aa_mix_nid); - if (idx >= 0) { - if (have_mute(codec, nid, HDA_INPUT)) { - unsigned int mute = with_aa_mix ? - AMP_IN_UNMUTE(idx) : AMP_IN_MUTE(idx); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - mute); - /* exclusively via aa-mix for front */ - if (pre_nid == spec->multiout.dac_nids[0]) { - num = snd_hda_get_conn_list(codec, nid, - NULL); - for (j = 0; j < num; j++) { - if (j == idx) - continue; - snd_hda_codec_write(codec, - nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(j)); - } - } - } - break; - } - } + activate_output_path(codec, path, true, force); } static void via_auto_init_multi_out(struct hda_codec *codec) -- cgit v1.2.3 From 5c9a5615dedec19196b1217e864616a2ce9e392a Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Fri, 8 Jul 2011 14:03:43 +0800 Subject: ALSA: hda - Fix DAC checks for VT2002P/1802/1812 codecs For VT2002P, VT1802 and VT1812 codecs, there're only two DACs. So smart51 control shouldn't be created. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 8f59e0b5d477..b289abf0db55 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1716,18 +1716,21 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec) { struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; + int i, dac_num; hda_nid_t nid; spec->multiout.dac_nids = spec->private_dac_nids; - spec->multiout.num_dacs = cfg->line_outs; + dac_num = 0; for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; if (!nid) continue; - if (parse_output_path(codec, nid, 0, &spec->out_path[i])) + if (parse_output_path(codec, nid, 0, &spec->out_path[i])) { spec->private_dac_nids[i] = spec->out_path[i].path[0]; + dac_num++; + } } + spec->multiout.num_dacs = dac_num; return 0; } @@ -1838,6 +1841,10 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (err < 0) return err; + if (spec->multiout.num_dacs < 3) { + spec->smart51_nums = 0; + cfg->line_outs = old_line_outs; + } for (i = 0; i < cfg->line_outs; i++) { hda_nid_t pin, dac; pin = cfg->line_out_pins[i]; @@ -3383,6 +3390,7 @@ static int patch_vt2002P(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x21; + spec->dac_mixer_idx = 3; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); -- cgit v1.2.3 From a2a870c82797e47884b2736e95e9d9c89a51c219 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Fri, 8 Jul 2011 14:04:33 +0800 Subject: ALSA: hda - Fix Independent-HP detection on VT2002P/1802/1812 codecs For VT2002P, VT1802 and VT1812 codecs, to create Independent HP control. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b289abf0db55..dbc862e4ff13 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1915,6 +1915,12 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) &spec->hp_path)) { spec->hp_dac_nid = spec->hp_path.path[0]; spec->hp_indep_shared = true; + } else if (spec->multiout.dac_nids[HDA_CLFE] && + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_CLFE], + &spec->hp_path)) { + spec->hp_dac_nid = spec->hp_path.path[0]; + spec->hp_indep_shared = true; } if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], -- cgit v1.2.3 From e59ea3ed9fe0cde526c004441465d13287426b29 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Jun 2011 17:21:00 +0200 Subject: ALSA: hda - Add a fix-up for HP RP5800 The BIOS provides bogus pin configs, and also invalid SSID. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c1adb3bce7e8..a29e6b3c6a70 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6126,6 +6126,7 @@ enum { ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, ALC662_FIXUP_SKU_IGNORE, + ALC662_FIXUP_HP_RP5800, }; static const struct alc_fixup alc662_fixups[] = { @@ -6158,12 +6159,22 @@ static const struct alc_fixup alc662_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, + [ALC662_FIXUP_HP_RP5800] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0221201f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), -- cgit v1.2.3 From afcd551508dcdb38e80728137c1c166d19bd47dd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 11:07:59 +0200 Subject: ALSA: hda - Merge ALC680 auto-parser to the standard parser Improved the standard Realtek auto-parser to support the codec topology like ALC680. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 231 ++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 141 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a29e6b3c6a70..f82333b11e41 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2682,6 +2682,8 @@ static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) hda_nid_t list[5]; int i, num; + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT) + return nid; num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); for (i = 0; i < num; i++) { if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) @@ -2838,6 +2840,8 @@ static int alc_auto_add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, hda_nid_t nid, unsigned int chs) { + if (!nid) + return 0; return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } @@ -2852,9 +2856,16 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, hda_nid_t nid, unsigned int chs) { + int wid_type; int type; unsigned long val; - if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { + if (!nid) + return 0; + wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); + } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { type = ALC_CTL_WIDGET_MUTE; val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); } else { @@ -2867,12 +2878,42 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, #define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \ alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3) +#define nid_has_mute(codec, nid, dir) \ + (query_amp_caps(codec, nid, dir) & AC_AMPCAP_MUTE) +#define nid_has_volume(codec, nid, dir) \ + (query_amp_caps(codec, nid, dir) & AC_AMPCAP_NUM_STEPS) + +static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); + if (nid_has_mute(codec, pin, HDA_OUTPUT)) + return pin; + else if (mix && nid_has_mute(codec, mix, HDA_INPUT)) + return mix; + else if (nid_has_mute(codec, dac, HDA_OUTPUT)) + return dac; + return 0; +} + +static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); + if (nid_has_volume(codec, dac, HDA_OUTPUT)) + return dac; + else if (nid_has_volume(codec, mix, HDA_OUTPUT)) + return mix; + else if (nid_has_volume(codec, pin, HDA_OUTPUT)) + return pin; + return 0; +} + /* add playback controls from the parsed DAC table */ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct alc_spec *spec = codec->spec; - hda_nid_t nid, mix, pin; int i, err, noutputs; noutputs = cfg->line_outs; @@ -2882,36 +2923,39 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, for (i = 0; i < noutputs; i++) { const char *name; int index; - nid = spec->multiout.dac_nids[i]; - if (!nid) + hda_nid_t dac, pin; + hda_nid_t sw, vol; + + dac = spec->multiout.dac_nids[i]; + if (!dac) continue; if (i >= cfg->line_outs) pin = spec->multi_io[i - 1].pin; else pin = cfg->line_out_pins[i]; - mix = alc_auto_dac_to_mix(codec, pin, nid); - if (!mix) - continue; + + sw = alc_look_for_out_mute_nid(codec, pin, dac); + vol = alc_look_for_out_vol_nid(codec, pin, dac); name = alc_get_line_out_pfx(spec, i, true, &index); if (!name) { /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, nid, 1); + err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); if (err < 0) return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, nid, 2); + err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, mix, 1); + err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, mix, 2); + err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2); if (err < 0) return err; } else { - err = alc_auto_add_stereo_vol(codec, name, index, nid); + err = alc_auto_add_stereo_vol(codec, name, index, vol); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, name, index, mix); + err = alc_auto_add_stereo_sw(codec, name, index, sw); if (err < 0) return err; } @@ -2924,7 +2968,7 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac, const char *pfx) { struct alc_spec *spec = codec->spec; - hda_nid_t mix; + hda_nid_t sw, vol; int err; if (!pin) @@ -2938,13 +2982,12 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } - mix = alc_auto_dac_to_mix(codec, pin, dac); - if (!mix) - return 0; - err = alc_auto_add_stereo_vol(codec, pfx, 0, dac); + sw = alc_look_for_out_mute_nid(codec, pin, dac); + vol = alc_look_for_out_vol_nid(codec, pin, dac); + err = alc_auto_add_stereo_vol(codec, pfx, 0, vol); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, pfx, 0, mix); + err = alc_auto_add_stereo_sw(codec, pfx, 0, sw); if (err < 0) return err; return 0; @@ -2967,15 +3010,15 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) } static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, + hda_nid_t pin, int pin_type, hda_nid_t dac) { int i, num; - hda_nid_t mix = 0; + hda_nid_t nid, mix = 0; hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - alc_set_pin_output(codec, nid, pin_type); - nid = alc_go_down_to_selector(codec, nid); + alc_set_pin_output(codec, pin, pin_type); + nid = alc_go_down_to_selector(codec, pin); num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); for (i = 0; i < num; i++) { if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) @@ -2990,19 +3033,17 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, if (num > 1) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); /* unmute mixer widget inputs */ - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + if (nid_has_mute(codec, mix, HDA_INPUT)) { + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)); + } /* initialize volume */ - if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) - nid = dac; - else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) - nid = mix; - else - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); + nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (nid) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); } static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -6333,112 +6374,7 @@ static int patch_alc899(struct hda_codec *codec) /* * ALC680 support */ -/* 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->private_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; - int index; - name = alc_get_line_out_pfx(spec, 0, true, &index); - 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); -} - -/* - * BIOS auto configuration - */ static int alc680_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -6458,7 +6394,20 @@ static int alc680_parse_auto_config(struct hda_codec *codec) } return 0; /* can't find valid BIOS pin config */ } - err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg); + + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; @@ -6489,8 +6438,8 @@ static int alc680_parse_auto_config(struct hda_codec *codec) 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); + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec); -- cgit v1.2.3 From e47706295843730c815dbc98f47ed2f75e051109 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 11:11:35 +0200 Subject: ALSA: hda - Provide the standard auto_init for Realtek codecs Remove redundant definitions. Ideally, all init functions should be identical in future. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 105 +++++++++--------------------------------- 1 file changed, 21 insertions(+), 84 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f82333b11e41..e230947cd3fe 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3468,6 +3468,21 @@ static void set_capture_mixer(struct hda_codec *codec) } } +/* + * standard auto-parser initializations + */ +static void alc_auto_init_std(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); +} + /* * Digital-beep handlers */ @@ -3549,19 +3564,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec) return 1; } -/* additional initialization for auto-configuration model */ -static void alc880_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list alc880_loopbacks[] = { { 0x0b, HDA_INPUT, 0 }, @@ -3655,7 +3657,7 @@ static int patch_alc880(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc880_auto_init; + spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc880_loopbacks; @@ -4075,19 +4077,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec) return 1; /* config found */ } -/* additional initialization for auto-configuration model */ -static void alc882_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - /* */ #ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS @@ -4182,7 +4171,7 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc882_auto_init; + spec->init_hook = alc_auto_init_std; alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -4393,19 +4382,6 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { #define alc262_loopbacks alc880_loopbacks #endif -/* init callback for auto-configuration model -- overriding the default init */ -static void alc262_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - /* */ #ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS @@ -4500,7 +4476,7 @@ static int patch_alc262(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc262_auto_init; + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; alc_init_jacks(codec); @@ -5924,19 +5900,6 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) return 1; } -/* additional initialization for auto-configuration model */ -static void alc861vd_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - enum { ALC660VD_FIX_ASUS_GPIO1 }; @@ -6045,7 +6008,7 @@ static int patch_alc861vd(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc861vd_auto_init; + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) @@ -6134,19 +6097,6 @@ static int alc662_parse_auto_config(struct hda_codec *codec) return 1; } -/* additional initialization for auto-configuration model */ -static void alc662_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - static void alc272_fixup_mario(struct hda_codec *codec, const struct alc_fixup *fix, int action) { @@ -6332,7 +6282,7 @@ static int patch_alc662(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc662_auto_init; + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; alc_init_jacks(codec); @@ -6434,19 +6384,6 @@ static int alc680_parse_auto_config(struct hda_codec *codec) return 1; } -/* 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; - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - /* */ #ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS @@ -6513,7 +6450,7 @@ static int patch_alc680(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc680_auto_init; + spec->init_hook = alc_auto_init_std; return 0; } -- cgit v1.2.3 From 21d45d2ba97fa5bcb41b444095338dde792026d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 11:35:11 +0200 Subject: ALSA: hda - Fix Oops in smart51 parsing in VIA codec Typical off-by-one thinko. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index dbc862e4ff13..d051cb53dd86 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1802,7 +1802,7 @@ static void mangle_smart51(struct hda_codec *codec) for (j = 0; j < nums; j++) if (ins[pins[j]].type < ins[i].type) { memmove(pins + j + 1, pins + j, - (nums - j - 1) * sizeof(int)); + (nums - j) * sizeof(int)); break; } pins[j] = i; -- cgit v1.2.3 From 28dc10a5f1bebfbb7cb19f588bc1652a00992402 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Fri, 8 Jul 2011 18:28:47 +0800 Subject: ALSA: hda - Fix output-path of VT1812 codec For VT1812, add dac_mixer_idx for initialization. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d051cb53dd86..0da4f8ff5420 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -3534,6 +3534,7 @@ static int patch_vt1812(struct hda_codec *codec) spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); + spec->dac_mixer_idx = 5; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); -- cgit v1.2.3 From 268ff6fbe70a4ab3c931caa0fdffc3d49265d135 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 14:37:35 +0200 Subject: ALSA: hda - Fix auto-mic detection in Realtek codec-parser A regression fix from commit 21268961d3d1bbdd22a19b68adb80119e8c72dcd ALSA: hda - More flexible dynamic-ADC switching for Realtek codecs The auto-mic wasn't detected properly when no ADC-switch is needed. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e230947cd3fe..371d1e418d53 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -958,7 +958,7 @@ static bool alc_check_dyn_adc_switch(struct hda_codec *codec) break; } if (i >= imux->num_items) - return false; /* no ADC-switch is needed */ + return true; /* no ADC-switch is needed */ } for (i = 0; i < imux->num_items; i++) { -- cgit v1.2.3 From a1f649d5475f6fa7ea5707510ec8b2e3019f38dd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 14:39:03 +0200 Subject: ALSA: hda - Merge ALC861-VD auto-parse to the standard parser The existing standard auto-parser can work well with this codec, too. Let's merge. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 141 +----------------------------------------- 1 file changed, 3 insertions(+), 138 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 371d1e418d53..3b2964e0ce07 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5714,137 +5714,6 @@ static int patch_alc861(struct hda_codec *codec) #define alc861vd_loopbacks alc880_loopbacks #endif -/* - * BIOS auto configuration - */ -#define alc861vd_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) -#define alc861vd_fixed_pin_idx(nid) ((nid) - 0x14) -#define alc861vd_is_multi_pin(nid) ((nid) >= 0x18) -#define alc861vd_multi_pin_idx(nid) ((nid) - 0x18) -#define alc861vd_idx_to_dac(nid) ((nid) + 0x02) -#define alc861vd_dac_to_idx(nid) ((nid) - 0x02) -#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) -#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) - -/* add playback controls from the parsed DAC table */ -/* Based on ALC880 version. But ALC861VD has separate, - * different NIDs for mute/unmute switch and volume control */ -static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid_v, nid_s; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - if (!spec->multiout.dac_nids[i]) - continue; - nid_v = alc861vd_idx_to_mixer_vol( - alc861vd_dac_to_idx( - spec->multiout.dac_nids[i])); - nid_s = alc861vd_idx_to_mixer_switch( - alc861vd_dac_to_idx( - spec->multiout.dac_nids[i])); - - name = alc_get_line_out_pfx(spec, i, true, &index); - if (!name) { - /* Center/LFE */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "Center", - HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "LFE", - HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "Center", - HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, - HDA_INPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "LFE", - HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, - HDA_INPUT)); - if (err < 0) - return err; - } else { - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, index, - HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, index, - HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, - HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -/* Based on ALC880 version. But ALC861VD has separate, - * different NIDs for mute/unmute switch and volume control */ -static int alc861vd_auto_create_extra_out(struct alc_spec *spec, - hda_nid_t pin, const char *pfx) -{ - hda_nid_t nid_v, nid_s; - int err; - - if (!pin) - return 0; - - if (alc861vd_is_fixed_pin(pin)) { - nid_v = alc861vd_idx_to_dac(alc861vd_fixed_pin_idx(pin)); - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid_v; - else - spec->multiout.extra_out_nid[0] = nid_v; - /* control HP volume/switch on the output mixer amp */ - nid_v = alc861vd_idx_to_mixer_vol( - alc861vd_fixed_pin_idx(pin)); - nid_s = alc861vd_idx_to_mixer_switch( - alc861vd_fixed_pin_idx(pin)); - - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, - HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } else if (alc861vd_is_multi_pin(pin)) { - /* set manual connection */ - /* we have only a switch on HP-out PIN */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -/* parse the BIOS configuration and set up the alc_spec - * return 1 if successful, 0 if the proper config is not found, - * or a negative error code - * Based on ALC880 version - had to change it to override - * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ static int alc861vd_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5864,17 +5733,13 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; - err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc861vd_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); + err = alc_auto_create_hp_out(codec); if (err < 0) return err; - err = alc861vd_auto_create_extra_out(spec, - spec->autocfg.hp_pins[0], - "Headphone"); + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); -- cgit v1.2.3 From 44c0240052892911d9ebcb2bbc2a5cfc3176077c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 15:14:19 +0200 Subject: ALSA: hda - Fix amp-cap checks in patch_realtek.c query_amp_caps() may return non-zero if the amp cap isn't supported by the codec. Thus one needs to check widget-caps first, then check the corresponding amp-caps. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3b2964e0ce07..db9df5759103 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -206,6 +206,22 @@ struct alc_spec { #define ALC_MODEL_AUTO 0 /* common for all chips */ +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits) +{ + if (!nid) + return false; + if (get_wcaps(codec, nid) & (1 << (dir + 1))) + if (query_amp_caps(codec, nid, dir) & bits) + return true; + return false; +} + +#define nid_has_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) +#define nid_has_volume(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) + /* * input MUX handling */ @@ -2637,7 +2653,8 @@ static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); /* unmute pin */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); } @@ -2878,11 +2895,6 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, #define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \ alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3) -#define nid_has_mute(codec, nid, dir) \ - (query_amp_caps(codec, nid, dir) & AC_AMPCAP_MUTE) -#define nid_has_volume(codec, nid, dir) \ - (query_amp_caps(codec, nid, dir) & AC_AMPCAP_NUM_STEPS) - static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { @@ -3310,7 +3322,7 @@ static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) nid = spec->adc_nids[adc_idx]; /* mute ADC */ - if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) { + if (nid_has_mute(codec, nid, HDA_INPUT)) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); @@ -3319,7 +3331,7 @@ static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) if (!spec->capsrc_nids) return; nid = spec->capsrc_nids[adc_idx]; - if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) + if (nid_has_mute(codec, nid, HDA_OUTPUT)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); @@ -3436,12 +3448,10 @@ static void set_capture_mixer(struct hda_codec *codec) }; /* check whether either of ADC or MUX has a volume control */ - if (!(query_amp_caps(codec, spec->adc_nids[0], HDA_INPUT) & - AC_AMPCAP_NUM_STEPS)) { + if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) { if (!spec->capsrc_nids) return; /* no volume */ - if (!(query_amp_caps(codec, spec->capsrc_nids[0], HDA_OUTPUT) & - AC_AMPCAP_NUM_STEPS)) + if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT)) return; /* no volume in capsrc, too */ spec->vol_in_capsrc = 1; } -- cgit v1.2.3 From 72dcd8e76bd2b5d9846c3103ec020e1b550cdaac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 15:16:55 +0200 Subject: ALSA: hda - Merge ALC861 auto-parser code Merge more auto-parser code in patch_realtek.c, now for ALC861. The topology of this codec is pretty simple, and can be parsed well by the current starndard parser. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 195 ++---------------------------------------- 1 file changed, 8 insertions(+), 187 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index db9df5759103..b9e0c73cbd76 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5364,176 +5364,6 @@ static int patch_alc269(struct hda_codec *codec) * ALC861 */ -static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t mix, srcs[5]; - int i, num; - - if (snd_hda_get_connections(codec, pin, &mix, 1) != 1) - return 0; - num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); - if (num < 0) - return 0; - for (i = 0; i < num; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, srcs[i])); - if (type != AC_WID_AUD_OUT) - continue; - if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids, - spec->multiout.num_dacs)) - return srcs[i]; - } - return 0; -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc861_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - hda_nid_t nid, dac; - - spec->multiout.dac_nids = spec->private_dac_nids; - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - dac = alc861_look_for_dac(codec, nid); - if (!dac) - continue; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - return 0; -} - -static int __alc861_create_out_sw(struct hda_codec *codec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) -{ - return __add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); -} - -#define alc861_create_out_sw(codec, pfx, nid, chs) \ - __alc861_create_out_sw(codec, pfx, nid, 0, chs) - -/* add playback controls from the parsed DAC table */ -static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - nid = spec->multiout.dac_nids[i]; - if (!nid) - continue; - name = alc_get_line_out_pfx(spec, i, true, &index); - if (!name) { - /* Center/LFE */ - err = alc861_create_out_sw(codec, "Center", nid, 1); - if (err < 0) - return err; - err = alc861_create_out_sw(codec, "LFE", nid, 2); - if (err < 0) - return err; - } else { - err = __alc861_create_out_sw(codec, name, nid, index, 3); - if (err < 0) - return err; - } - } - return 0; -} - -static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int err; - hda_nid_t nid; - - if (!pin) - return 0; - - if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) { - nid = alc861_look_for_dac(codec, pin); - if (nid) { - err = alc861_create_out_sw(codec, "Headphone", nid, 3); - if (err < 0) - return err; - spec->multiout.hp_nid = nid; - } - } - return 0; -} - -static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, - int pin_type, hda_nid_t dac) -{ - hda_nid_t mix, srcs[5]; - int i, num; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - if (snd_hda_get_connections(codec, nid, &mix, 1) != 1) - return; - num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); - if (num < 0) - return; - for (i = 0; i < num; i++) { - unsigned int mute; - if (srcs[i] == dac || srcs[i] == 0x15) - mute = AMP_IN_UNMUTE(i); - else - mute = AMP_IN_MUTE(i); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - mute); - } -} - -static void alc861_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc861_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} - -static void alc861_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->autocfg.hp_outs) - alc861_auto_set_output_and_unmute(codec, - spec->autocfg.hp_pins[0], - PIN_HP, - spec->multiout.hp_nid); - if (spec->autocfg.speaker_outs) - alc861_auto_set_output_and_unmute(codec, - spec->autocfg.speaker_pins[0], - PIN_OUT, - spec->multiout.dac_nids[0]); -} - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ static int alc861_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5547,16 +5377,19 @@ static int alc861_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc861_auto_fill_dac_nids(codec); + err = alc_auto_fill_dac_nids(codec); if (err < 0) return err; - err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids); + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg); + err = alc_auto_create_hp_out(codec); if (err < 0) return err; - err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); @@ -5580,18 +5413,6 @@ static int alc861_parse_auto_config(struct hda_codec *codec) return 1; } -/* additional initialization for auto-configuration model */ -static void alc861_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc861_auto_init_multi_out(codec); - alc861_auto_init_hp_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list alc861_loopbacks[] = { { 0x15, HDA_INPUT, 0 }, @@ -5700,7 +5521,7 @@ static int patch_alc861(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) { - spec->init_hook = alc861_auto_init; + spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->power_hook = alc_power_eapd; #endif -- cgit v1.2.3 From be9bc37bccab8c492e6cbaaa4d5b1b2c8296b1c4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 16:01:47 +0200 Subject: ALSA: hda - Merge ALC268/269 auto-parser codes Now coming to ALC268/269 parser codes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 265 +++++------------------------------------- 1 file changed, 26 insertions(+), 239 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b9e0c73cbd76..8f1bd80e6027 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3541,9 +3541,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec) return 0; /* can't find valid BIOS pin config */ err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); @@ -4501,204 +4498,6 @@ static int patch_alc262(struct hda_codec *codec) /* * ALC268 */ -/* create input playback/capture controls for the given pin */ -static int alc268_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: - case 0x16: - dac = 0x02; - break; - case 0x15: - case 0x1a: /* ALC259/269 only */ - case 0x1b: /* ALC259/269 only */ - case 0x21: /* ALC269vb has this pin, too */ - dac = 0x03; - break; - default: - snd_printd(KERN_WARNING "hda_codec: " - "ignoring pin 0x%x as unknown\n", nid); - 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; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - - if (nid != 0x16) - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); - else /* mono */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); - if (err < 0) - return err; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc268_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; - int index; - name = alc_get_line_out_pfx(spec, 0, true, &index); - err = alc268_new_analog_output(spec, nid, name, 0); - if (err < 0) - return err; - } - - nid = cfg->speaker_pins[0]; - if (nid == 0x1d) { - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } else if (nid) { - err = alc268_new_analog_output(spec, nid, "Speaker", 0); - if (err < 0) - return err; - } - nid = cfg->hp_pins[0]; - if (nid) { - err = alc268_new_analog_output(spec, nid, "Headphone", 0); - if (err < 0) - return err; - } - - nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; - if (nid == 0x16) { - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type) -{ - int idx; - - alc_set_pin_output(codec, nid, pin_type); - if (snd_hda_get_conn_list(codec, nid, NULL) <= 1) - return; - if (nid == 0x14 || nid == 0x16) - idx = 0; - else - idx = 1; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid) -{ - if (!nid) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); -} - -static void alc268_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc268_auto_set_output_and_unmute(codec, nid, pin_type); - } - /* mute DACs */ - for (i = 0; i < spec->multiout.num_dacs; i++) - alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]); -} - -static void alc268_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - int i; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - pin = spec->autocfg.hp_pins[i]; - alc268_auto_set_output_and_unmute(codec, pin, PIN_HP); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - pin = spec->autocfg.speaker_pins[i]; - alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT); - } - if (spec->autocfg.mono_out_pin) - snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* mute DACs */ - alc268_auto_init_dac(codec, spec->multiout.hp_nid); - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) - alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]); -} - -static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; - hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; - hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; - unsigned int dac_vol1, dac_vol2; - - if (line_nid == 0x1d || speaker_nid == 0x1d) { - snd_hda_codec_write(codec, speaker_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* mute mixer inputs from 0x1d */ - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } else { - /* unmute mixer inputs from 0x1d */ - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); - } - - dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */ - if (line_nid == 0x14) - dac_vol2 = AMP_OUT_ZERO; - else if (line_nid == 0x15) - dac_vol1 = AMP_OUT_ZERO; - if (hp_nid == 0x14) - dac_vol2 = AMP_OUT_ZERO; - else if (hp_nid == 0x15) - dac_vol1 = AMP_OUT_ZERO; - if (line_nid != 0x16 || hp_nid != 0x16 || - spec->autocfg.line_out_pins[1] != 0x16 || - spec->autocfg.line_out_pins[2] != 0x16) - dac_vol1 = dac_vol2 = AMP_OUT_ZERO; - - snd_hda_codec_write(codec, 0x02, 0, - AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1); - snd_hda_codec_write(codec, 0x03, 0, - AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); -} - /* bind Beep switches of both NID 0x0f and 0x10 */ static const struct hda_bind_ctls alc268_bind_beep_sw = { .ops = &snd_hda_bind_sw, @@ -4744,7 +4543,20 @@ static int alc268_parse_auto_config(struct hda_codec *codec) } return 0; /* can't find valid BIOS pin config */ } - err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); + + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); @@ -4776,20 +4588,6 @@ static int alc268_parse_auto_config(struct hda_codec *codec) return 1; } -/* init callback for auto-configuration model -- overriding the default init */ -static void alc268_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc268_auto_init_multi_out(codec); - alc268_auto_init_hp_out(codec); - alc268_auto_init_mono_speaker_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - /* */ #ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS @@ -4879,7 +4677,7 @@ static int patch_alc268(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc268_auto_init; + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; alc_init_jacks(codec); @@ -4890,9 +4688,6 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ -#define alc269_auto_create_multi_out_ctls \ - alc268_auto_create_multi_out_ctls - #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc269_loopbacks alc880_loopbacks #endif @@ -4968,7 +4763,16 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); @@ -5000,23 +4804,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc269_auto_init_multi_out alc268_auto_init_multi_out -#define alc269_auto_init_hp_out alc268_auto_init_hp_out - - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc269_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc269_auto_init_multi_out(codec); - alc269_auto_init_hp_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) { int val = alc_read_coef_idx(codec, 0x04); @@ -5346,7 +5133,7 @@ static int patch_alc269(struct hda_codec *codec) codec->patch_ops.resume = alc269_resume; #endif if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc269_auto_init; + spec->init_hook = alc_auto_init_std; spec->shutup = alc269_shutup; alc_init_jacks(codec); -- cgit v1.2.3 From 4c11398edc19fdd9c651f3ff287cd628fecaf574 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 16:12:05 +0200 Subject: ALSA: hda - Merge ALC269 parser code One more code reduction. This codec has less DACs, thus the wiring to DAC can't be filled uniquely for all output pins, i.e. some outputs share the same volume control. Except for that, all seems working fine. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 124 ++++-------------------------------------- 1 file changed, 10 insertions(+), 114 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8f1bd80e6027..2a94c58b2104 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4193,119 +4193,6 @@ static int patch_alc882(struct hda_codec *codec) /* * ALC262 support */ - -/* We use two mixers depending on the output pin; 0x16 is a mono output - * and thus it's bound with a different mixer. - * This function returns which mixer amp should be used. - */ -static int alc262_check_volbit(hda_nid_t nid) -{ - if (!nid) - return 0; - else if (nid == 0x16) - return 2; - else - return 1; -} - -static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vbits, int idx) -{ - unsigned long val; - int vbit; - - vbit = alc262_check_volbit(nid); - if (!vbit) - return 0; - if (*vbits & vbit) /* a volume control for this mixer already there */ - return 0; - *vbits |= vbit; - if (vbit == 2) - val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); - return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val); -} - -static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int idx) -{ - unsigned long val; - - if (!nid) - return 0; - if (nid == 0x16) - val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val); -} - -/* add playback controls from the parsed DAC table */ -static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - const char *pfx; - int vbits; - int i, index, err; - - spec->multiout.num_dacs = 1; /* only use one dac */ - spec->multiout.dac_nids = spec->private_dac_nids; - spec->private_dac_nids[0] = 2; - - for (i = 0; i < 2; i++) { - pfx = alc_get_line_out_pfx(spec, i, true, &index); - if (!pfx) - pfx = "PCM"; - err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, - index); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i], - "Speaker", i); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i], - "Headphone", i); - if (err < 0) - return err; - } - } - - vbits = alc262_check_volbit(cfg->line_out_pins[0]) | - alc262_check_volbit(cfg->speaker_pins[0]) | - alc262_check_volbit(cfg->hp_pins[0]); - vbits = 0; - for (i = 0; i < 2; i++) { - pfx = alc_get_line_out_pfx(spec, i, true, &index); - if (!pfx) - pfx = "PCM"; - err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx, - &vbits, i); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i], - "Speaker", &vbits, i); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i], - "Headphone", &vbits, i); - if (err < 0) - return err; - } - } - return 0; -} - -/* - * BIOS auto configuration - */ static int alc262_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -4324,7 +4211,16 @@ static int alc262_parse_auto_config(struct hda_codec *codec) } return 0; /* can't find valid BIOS pin config */ } - err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; err = alc_auto_create_input_ctls(codec); -- cgit v1.2.3 From 8452a982fb8a1d02d755a53a913c087a0d31aa18 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 16:19:48 +0200 Subject: ALSA: hda - Merge ALC260 auto-parser code Finally the last one. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 153 +++--------------------------------------- 1 file changed, 11 insertions(+), 142 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2a94c58b2104..10de78d8bc2a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3677,132 +3677,6 @@ static int patch_alc880(struct hda_codec *codec) /* * ALC260 support */ - -/* convert from pin to volume-mixer widget */ -static hda_nid_t alc260_pin_to_vol_mix(hda_nid_t nid) -{ - if (nid >= 0x0f && nid <= 0x11) - return nid - 0x7; - else if (nid >= 0x12 && nid <= 0x15) - return 0x08; - else - return 0; -} - -static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vol_bits) -{ - hda_nid_t nid_vol; - unsigned long vol_val, sw_val; - int chs, err; - - nid_vol = alc260_pin_to_vol_mix(nid); - if (!nid_vol) - return 0; /* N/A */ - if (nid == 0x11) - chs = 2; - else - chs = 3; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, chs, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - - if (!(*vol_bits & (1 << nid_vol))) { - /* first control for the volume widget */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); - if (err < 0) - return err; - *vol_bits |= (1 << nid_vol); - } - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); - if (err < 0) - return err; - return 1; -} - -/* add playback controls from the parsed DAC table */ -static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int err; - int vols = 0; - - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - spec->private_dac_nids[0] = 0x02; - - nid = cfg->line_out_pins[0]; - if (nid) { - const char *pfx; - int index; - pfx = alc_get_line_out_pfx(spec, 0, true, &index); - err = alc260_add_playback_controls(spec, nid, pfx, &vols); - if (err < 0) - return err; - } - - nid = cfg->speaker_pins[0]; - if (nid) { - err = alc260_add_playback_controls(spec, nid, "Speaker", &vols); - if (err < 0) - return err; - } - - nid = cfg->hp_pins[0]; - if (nid) { - err = alc260_add_playback_controls(spec, nid, "Headphone", - &vols); - if (err < 0) - return err; - } - return 0; -} - -static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int sel_idx) -{ - hda_nid_t mix; - - alc_set_pin_output(codec, nid, pin_type); - /* need the manual connection? */ - if (nid >= 0x12) { - int idx = nid - 0x12; - snd_hda_codec_write(codec, idx + 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, sel_idx); - } - - mix = alc260_pin_to_vol_mix(nid); - if (!mix) - return; - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); -} - -static void alc260_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - - nid = spec->autocfg.line_out_pins[0]; - if (nid) { - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0); - } - - nid = spec->autocfg.speaker_pins[0]; - if (nid) - alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); - - nid = spec->autocfg.hp_pins[0]; - if (nid) - alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); -} - static int alc260_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3813,11 +3687,18 @@ static int alc260_parse_auto_config(struct hda_codec *codec) alc260_ignore); if (err < 0) return err; - err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); if (err < 0) return err; - if (!spec->kctls.list) - return 0; /* can't find valid BIOS pin config */ err = alc_auto_create_input_ctls(codec); if (err < 0) return err; @@ -3837,18 +3718,6 @@ static int alc260_parse_auto_config(struct hda_codec *codec) return 1; } -/* additional initialization for auto-configuration model */ -static void alc260_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc260_auto_init_multi_out(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list alc260_loopbacks[] = { { 0x07, HDA_INPUT, 0 }, @@ -3954,7 +3823,7 @@ static int patch_alc260(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc260_auto_init; + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) -- cgit v1.2.3 From 3e6179b8445bf76123cfab1e0af4833cc7618a4a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Jul 2011 16:55:13 +0200 Subject: ALSA: hda - Merge alc*_parse_auto_config() functions in patch_realtek.c Now all alc*_parse_auto_config() do almost same thing except for the NID list to ignore and the PINs for SSID-check, we can merge all these to a single function. A good amount of code reduction. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 686 ++++++++++-------------------------------- 1 file changed, 160 insertions(+), 526 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 10de78d8bc2a..aaa27557e04f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1338,11 +1338,11 @@ do_sku: return 1; } -static void alc_ssid_check(struct hda_codec *codec, - hda_nid_t porta, hda_nid_t porte, - hda_nid_t portd, hda_nid_t porti) +/* Check the validity of ALC subsystem-id + * ports contains an array of 4 pin NIDs for port-A, E, D and I */ +static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) { - if (!alc_subsystem_id(codec, porta, porte, portd, porti)) { + if (!alc_subsystem_id(codec, ports[0], ports[1], ports[2], ports[3])) { struct alc_spec *spec = codec->spec; snd_printd("realtek: " "Enable default setup for auto mode as fallback\n"); @@ -3527,20 +3527,29 @@ static inline int has_cdefine_beep(struct hda_codec *codec) /* return 1 if successful, 0 if the proper config is not found, * or a negative error code */ -static int alc880_parse_auto_config(struct hda_codec *codec) +static int alc_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids, + const hda_nid_t *ssid_nids) { struct alc_spec *spec = codec->spec; int err; - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc880_ignore); + ignore_nids); if (err < 0) return err; - if (!spec->autocfg.line_outs) + 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 = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); if (err < 0) return err; err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); @@ -3558,19 +3567,35 @@ static int alc880_parse_auto_config(struct hda_codec *codec) spec->multiout.max_channels = spec->multiout.num_dacs * 2; + dig_only: alc_auto_parse_digital(codec); - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); + if (!spec->no_analog) + alc_remove_invalid_adc_nids(codec); + + if (ssid_nids) + alc_ssid_check(codec, ssid_nids); - alc_remove_invalid_adc_nids(codec); + if (!spec->no_analog) { + alc_auto_check_switches(codec); + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + } - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); return 1; } +static int alc880_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); +} + #ifdef CONFIG_SND_HDA_POWER_SAVE static const struct hda_amp_list alc880_loopbacks[] = { { 0x0b, HDA_INPUT, 0 }, @@ -3643,22 +3668,26 @@ static int patch_alc880(struct hda_codec *codec) #endif } - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc880_presets[board_config]); - if (!spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); + + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + } spec->vmaster_nid = 0x0c; @@ -3679,43 +3708,9 @@ static int patch_alc880(struct hda_codec *codec) */ static int alc260_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc260_ignore); - if (err < 0) - return err; - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); - alc_auto_check_switches(codec); - - return 1; + static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; + return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -3800,22 +3795,26 @@ static int patch_alc260(struct hda_codec *codec) #endif } - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc260_presets[board_config]); - if (!spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - set_capture_mixer(codec); - set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); + + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); + + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -3904,53 +3903,9 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { /* almost identical with ALC880 parser... */ static int alc882_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc882_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - return 1; /* config found */ + static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); } /* @@ -4019,27 +3974,26 @@ static int patch_alc882(struct hda_codec *codec) #endif } - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc882_presets[board_config]); - if (!spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - set_capture_mixer(codec); + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); - if (has_cdefine_beep(codec)) + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -4064,56 +4018,9 @@ static int patch_alc882(struct hda_codec *codec) */ static int alc262_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc262_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 = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - return 1; + static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); } /* @@ -4221,26 +4128,26 @@ static int patch_alc262(struct hda_codec *codec) #endif } - if (!spec->no_analog && has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc262_presets[board_config]); - if (!spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer && !spec->no_analog) + + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) + + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -4292,65 +4199,16 @@ static const struct hda_verb alc268_beep_init_verbs[] = { */ static int alc268_parse_auto_config(struct hda_codec *codec) { + static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; struct alc_spec *spec = codec->spec; - int err; - static const 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) { - if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; + int err = alc_parse_auto_config(codec, NULL, alc268_ssids); + if (err > 0) { + if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { + add_mixer(spec, alc268_beep_mixer); + add_verb(spec, alc268_beep_init_verbs); } - return 0; /* can't find valid BIOS pin config */ } - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - dig_only: - /* digital only support output */ - alc_auto_parse_digital(codec); - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { - add_mixer(spec, alc268_beep_mixer); - add_verb(spec, alc268_beep_init_verbs); - } - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - return 1; + return err; } /* @@ -4435,7 +4293,7 @@ static int patch_alc268(struct hda_codec *codec) alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer && !spec->no_analog) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); spec->vmaster_nid = 0x02; @@ -4519,54 +4377,14 @@ enum { */ static int alc269_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; 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 = spec->codec_variant == ALC269_TYPE_ALC269VA ? + alc269va_ssids : alc269_ssids; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc269_ignore); - if (err < 0) - return err; - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - if (spec->codec_variant != ALC269_TYPE_ALC269VA) - alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); - else - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(codec); - - return 1; + return alc_parse_auto_config(codec, alc269_ignore, ssids); } static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) @@ -4857,14 +4675,6 @@ static int patch_alc269(struct hda_codec *codec) #endif } - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc269_presets[board_config]); @@ -4878,16 +4688,23 @@ static int patch_alc269(struct hda_codec *codec) } #endif - if (!spec->adc_nids) { /* wasn't filled automatically? use default */ + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - if (has_cdefine_beep(codec)) + + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -4918,51 +4735,9 @@ static int patch_alc269(struct hda_codec *codec) static int alc861_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc861_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); - alc_auto_check_switches(codec); - - set_capture_mixer(codec); - - return 1; + static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; + return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -5048,24 +4823,26 @@ static int patch_alc861(struct hda_codec *codec) #endif } - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) { - alc_free(codec); - return err; - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc861_presets[board_config]); - if (!spec->adc_nids) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x23); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + } spec->vmaster_nid = 0x03; @@ -5099,53 +4876,9 @@ static int patch_alc861(struct hda_codec *codec) static int alc861vd_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc861vd_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - return 1; + static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); } enum { @@ -5226,12 +4959,6 @@ static int patch_alc861vd(struct hda_codec *codec) #endif } - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) { - alc_free(codec); - return err; - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc861vd_presets[board_config]); @@ -5240,14 +4967,23 @@ static int patch_alc861vd(struct hda_codec *codec) add_verb(spec, alc660vd_eapd_verbs); } - if (!spec->adc_nids) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); + + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x23); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + } spec->vmaster_nid = 0x02; @@ -5287,62 +5023,17 @@ static int patch_alc861vd(struct hda_codec *codec) static int alc662_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc662_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_create_extra_out(codec, - spec->autocfg.speaker_pins[0], - spec->multiout.extra_out_nid[0], - "Speaker"); - if (err < 0) - return err; - err = alc_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], - spec->multiout.hp_nid, - "Headphone"); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); + static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; + static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + const hda_nid_t *ssids; if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670) - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21); + ssids = alc663_ssids; else - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - alc_auto_check_switches(codec); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - return 1; + ssids = alc662_ssids; + return alc_parse_auto_config(codec, alc662_ignore, ssids); } static void alc272_fixup_mario(struct hda_codec *codec, @@ -5489,27 +5180,24 @@ static int patch_alc662(struct hda_codec *codec) #endif } - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc662_presets[board_config]); - if (!spec->adc_nids) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - if (has_cdefine_beep(codec)) { + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } switch (codec->vendor_id) { case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -5575,61 +5263,7 @@ static int patch_alc899(struct hda_codec *codec) static int alc680_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; - static const 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 = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - - err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - dig_only: - /* digital only support output */ - alc_auto_parse_digital(codec); - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - alc_remove_invalid_adc_nids(codec); - - alc_auto_check_switches(codec); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - return 1; + return alc_parse_auto_config(codec, NULL, NULL); } /* @@ -5685,13 +5319,13 @@ static int patch_alc680(struct hda_codec *codec) #endif } - if (!spec->adc_nids) { + if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); } - if (!spec->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); spec->vmaster_nid = 0x02; -- cgit v1.2.3 From 017f2a104c7fed53a9b2534f0795f1f5af87674f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 9 Jul 2011 14:42:25 +0200 Subject: ALSA: hda - Implement 44kHz workaround for IdeadPad as fixup Instead of checking the model quirk, use a fixup table for workaround of 44kHz-fixed PCM for Lenovo IdeaPad with ALC269. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index aaa27557e04f..124c63f4c457 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4460,6 +4460,21 @@ static void alc271_fixup_dmic(struct hda_codec *codec, snd_hda_sequence_write(codec, verbs); } +static void alc269_fixup_pcm_44k(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action != ALC_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->stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -4469,6 +4484,7 @@ enum { ALC269_FIXUP_LENOVO_EAPD, ALC275_FIXUP_SONY_HWEQ, ALC271_FIXUP_DMIC, + ALC269_FIXUP_PCM_44K, }; static const struct alc_fixup alc269_fixups[] = { @@ -4527,9 +4543,14 @@ static const struct alc_fixup alc269_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc271_fixup_dmic, }, + [ALC269_FIXUP_PCM_44K] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_pcm_44k, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), @@ -4541,7 +4562,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), {} }; @@ -4678,16 +4699,6 @@ static int patch_alc269(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc269_presets[board_config]); -#if 0 - if (board_config == ALC269_QUANTA_FL1) { - /* Due to a hardware problem on Lenovo Ideadpad, we need to - * fix the sample rate of analog I/O to 44.1kHz - */ - spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; - } -#endif - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); -- cgit v1.2.3 From 21ce0b65272b85f122455818b0c69740945b451a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jul 2011 10:33:47 +0200 Subject: ALSA: hda - Via Fix speaker-mute checks in VIA driver When the line-jack is plugged/unplugged, the driver must check also the headphone jack state in addition to the line-out jack. Currently it checks only the line-out state and ignores the headphone. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 0da4f8ff5420..be2e57b44507 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1606,12 +1606,10 @@ static void via_unsol_event(struct hda_codec *codec, res &= ~VIA_JACK_EVENT; - if (res == VIA_HP_EVENT) + if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT) via_hp_automute(codec); else if (res == VIA_GPIO_EVENT) via_gpio_control(codec); - else if (res == VIA_LINE_EVENT) - via_line_automute(codec, false); } #ifdef SND_HDA_NEEDS_RESUME @@ -2535,7 +2533,6 @@ static int via_init(struct hda_codec *codec) via_auto_init_unsol_event(codec); via_hp_automute(codec); - via_line_automute(codec, false); return 0; } -- cgit v1.2.3 From 6e969d9155a4ee7bce800dfbee02099105ca5b97 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jul 2011 11:28:13 +0200 Subject: ALSA: hda - Set line-out pin-ctls properly when indep-HP mode changes When Independent-HP mode is changed for VIA, the driver needs to re-issue the auto-mute check so that the line-out pins are set properly without influence of HP pin state. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index be2e57b44507..27de53fb331e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -615,6 +615,7 @@ static void via_auto_init_speaker_out(struct hda_codec *codec) } static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); +static void via_hp_automute(struct hda_codec *codec); static void via_auto_init_analog_input(struct hda_codec *codec) { @@ -801,6 +802,7 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, /* update jack power state */ set_widgets_power_state(codec); + via_hp_automute(codec); return 1; } @@ -1532,19 +1534,18 @@ static void via_line_automute(struct hda_codec *codec, int present) static void via_hp_automute(struct hda_codec *codec) { int present = 0; + int nums; struct via_spec *spec = codec->spec; - if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { - int nums; + if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - if (spec->smart51_enabled) - nums = spec->autocfg.line_outs + spec->smart51_nums; - else - nums = spec->autocfg.line_outs; - toggle_output_mutes(codec, nums, - spec->autocfg.line_out_pins, - present); - } + + if (spec->smart51_enabled) + nums = spec->autocfg.line_outs + spec->smart51_nums; + else + nums = spec->autocfg.line_outs; + toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); + via_line_automute(codec, present); } -- cgit v1.2.3 From 9499473463628a1af4be5aea1ad8d35d3fd341b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jul 2011 11:36:44 +0200 Subject: ALSA: hda - Preserve input pin-ctl bits in HP-automute for VIA codec For smart51 pins, we need to preserve the input pin-control bits at auto-mute controls instead of overwriting zero or pin-out-only. Otherwise the VREF won't be set properly when smart51 is disabled again. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 27de53fb331e..77df2bedfb81 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1509,10 +1509,18 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins, hda_nid_t *pins, bool mute) { int i; - for (i = 0; i < num_pins; i++) + for (i = 0; i < num_pins; i++) { + unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (parm & AC_PINCTL_IN_EN) + continue; + if (mute) + parm &= ~AC_PINCTL_OUT_EN; + else + parm |= AC_PINCTL_OUT_EN; snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - mute ? 0 : PIN_OUT); + AC_VERB_SET_PIN_WIDGET_CONTROL, parm); + } } /* mute internal speaker if line-out is plugged */ -- cgit v1.2.3 From 19110595c89b2d606883b7cb99260c7e47fd2143 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jul 2011 14:46:44 +0200 Subject: ALSA: hda - Turn on extra EAPDs on Conexant codecs Some machines seem to use EAPD control of the unused pin for controlling the overall EAPD. Since the driver currently doesn't check the EAPD of unused pins, the EAPD isn't enabled. For avoiding such a problem, turn all extra EAPDs on as default. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4ca880bb68fa..884f67b8f4e0 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -155,6 +155,10 @@ struct conexant_spec { unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ unsigned int beep_amp; + + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -3901,6 +3905,38 @@ static void cx_auto_parse_beep(struct hda_codec *codec) #define cx_auto_parse_beep(codec) #endif +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return true; + return false; +} + +/* parse extra-EAPD that aren't assigned to any pins */ +static void cx_auto_parse_eapd(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + continue; + if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || + found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || + found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs)) + continue; + spec->eapds[spec->num_eapds++] = nid; + if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + break; + } +} + static int cx_auto_parse_auto_config(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -3914,6 +3950,7 @@ static int cx_auto_parse_auto_config(struct hda_codec *codec) cx_auto_parse_input(codec); cx_auto_parse_digital(codec); cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); return 0; } @@ -4001,6 +4038,8 @@ static void cx_auto_init_output(struct hda_codec *codec) } } cx_auto_update_speakers(codec); + /* turn on/off extra EAPDs, too */ + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); } static void cx_auto_init_input(struct hda_codec *codec) -- cgit v1.2.3 From b2f934a0dffd4153e9447ee9e0090e357a3d8b3b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Jul 2011 16:23:26 +0200 Subject: ALSA: hda - Add snd_hda_override_conn_list() helper function Add a function to add/modify the connection-list cache entry. It'll be useful to fix a buggy hardware result. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 99 +++++++++++++++++++++++++++++++++-------------- sound/pci/hda/hda_codec.h | 2 + 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7f8502388a82..d0deab1ed510 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -310,10 +310,23 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); -static bool add_conn_list(struct snd_array *array, hda_nid_t nid); + +/* look up the cached results */ +static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) +{ + int i, len; + for (i = 0; i < array->used; ) { + hda_nid_t *p = snd_array_elem(array, i); + if (nid == *p) + return p; + len = p[1]; + i += len + 2; + } + return NULL; +} /** - * snd_hda_get_connections - get connection list + * snd_hda_get_conn_list - get connection list * @codec: the HDA codec * @nid: NID to parse * @listp: the pointer to store NID list @@ -327,42 +340,31 @@ int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp) { struct snd_array *array = &codec->conn_lists; - int i, len, old_used; + int len, err; hda_nid_t list[HDA_MAX_CONNECTIONS]; hda_nid_t *p; + bool added = false; - /* look up the cached results */ - for (i = 0; i < array->used; ) { - p = snd_array_elem(array, i); - len = p[1]; - if (nid == *p) { - if (listp) - *listp = p + 2; - return len; - } - i += len + 2; + again: + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(array, nid); + if (p) { + if (listp) + *listp = p + 2; + return p[1]; } + if (snd_BUG_ON(added)) + return -EINVAL; + /* read the connection and add to the cache */ len = _hda_get_connections(codec, nid, list, HDA_MAX_CONNECTIONS); if (len < 0) return len; - - /* add to the cache */ - old_used = array->used; - if (!add_conn_list(array, nid) || !add_conn_list(array, len)) - goto error_add; - for (i = 0; i < len; i++) - if (!add_conn_list(array, list[i])) - goto error_add; - - p = snd_array_elem(array, old_used); - if (listp) - *listp = p + 2; - return len; - - error_add: - array->used = old_used; - return -ENOMEM; + err = snd_hda_override_conn_list(codec, nid, len, list); + if (err < 0) + return err; + added = true; + goto again; } EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); @@ -502,6 +504,43 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) return true; } +/** + * snd_hda_override_conn_list - add/modify the connection-list to cache + * @codec: the HDA codec + * @nid: NID to parse + * @len: number of connection list entries + * @list: the list of connection entries + * + * Add or modify the given connection-list to the cache. If the corresponding + * cache already exists, invalidate it and append a new one. + * + * Returns zero or a negative error code. + */ +int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, + const hda_nid_t *list) +{ + struct snd_array *array = &codec->conn_lists; + hda_nid_t *p; + int i, old_used; + + p = lookup_conn_list(array, nid); + if (p) + *p = -1; /* invalidate the old entry */ + + old_used = array->used; + if (!add_conn_list(array, nid) || !add_conn_list(array, len)) + goto error_add; + for (i = 0; i < len; i++) + if (!add_conn_list(array, list[i])) + goto error_add; + return 0; + + error_add: + array->used = old_used; + return -ENOMEM; +} +EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); + /** * snd_hda_get_conn_index - get the connection index of the given NID * @codec: the HDA codec diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 10d500d2ba33..e6bc16f66bce 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -905,6 +905,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp); +int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, + const hda_nid_t *list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, -- cgit v1.2.3 From 9e7717c9eb9da8dba98f36dd3c390a45375499b3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jul 2011 15:42:52 +0200 Subject: ALSA: hda - Always read raw connections for proc output In the codec proc outputs, read the raw connections instead of the cached connection list, i.e. proc files contain only raw values. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 20 ++++++++++++++------ sound/pci/hda/hda_codec.h | 2 ++ sound/pci/hda/hda_proc.c | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d0deab1ed510..25b90eec915a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -308,9 +308,6 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); -static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); - /* look up the cached results */ static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) { @@ -357,7 +354,7 @@ int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, return -EINVAL; /* read the connection and add to the cache */ - len = _hda_get_connections(codec, nid, list, HDA_MAX_CONNECTIONS); + len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS); if (len < 0) return len; err = snd_hda_override_conn_list(codec, nid, len, list); @@ -399,8 +396,19 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_connections); -static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) +/** + * snd_hda_get_raw_connections - copy connection list without cache + * @codec: the HDA codec + * @nid: NID to parse + * @conn_list: connection list array + * @max_conns: max. number of connections to store + * + * Like snd_hda_get_connections(), copy the connection list but without + * checking through the connection-list cache. + * Currently called only from hda_proc.c, so not exported. + */ +int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) { unsigned int parm; int i, conn_len, conns; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index e6bc16f66bce..f465e07a4879 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -903,6 +903,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns); int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index bfe74c2fb079..2be57b051aa2 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -636,7 +636,7 @@ static void print_codec_info(struct snd_info_entry *entry, wid_caps |= AC_WCAP_CONN_LIST; if (wid_caps & AC_WCAP_CONN_LIST) - conn_len = snd_hda_get_connections(codec, nid, conn, + conn_len = snd_hda_get_raw_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); if (wid_caps & AC_WCAP_IN_AMP) { -- cgit v1.2.3 From 30b4503378c976cf66201a1e81820519f6bd79ac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jul 2011 17:05:04 +0200 Subject: ALSA: hda - Expose secret DAC-AA connection of some VIA codecs VT1718S and co have a secret connection from DAC to AA-mix, which doesn't appear in the connection list obtained from the h/w. Currently the driver fixes the connection index locally at init, but now we can expose it statically via snd_hda_override_connections() so that this conection can be checked better by the parser in future. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 77df2bedfb81..5232abc341f8 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -132,7 +132,6 @@ struct via_spec { hda_nid_t hp_dac_nid; bool hp_indep_shared; /* indep HP-DAC is shared with side ch */ int num_active_streams; - int dac_mixer_idx; struct nid_path out_path[HDA_SIDE + 1]; struct nid_path hp_path; @@ -1881,8 +1880,6 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) idx = get_connection_index(codec, spec->aa_mix_nid, spec->multiout.dac_nids[0]); - if (idx < 0 && spec->dac_mixer_idx) - idx = spec->dac_mixer_idx; if (idx >= 0) { /* add control to mixer */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, @@ -3028,6 +3025,41 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) } } +/* Add a connection to the primary DAC from AA-mixer for some codecs + * This isn't listed from the raw info, but the chip has a secret connection. + */ +static int add_secret_dac_path(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i, nums; + hda_nid_t conn[8]; + hda_nid_t nid; + + if (!spec->aa_mix_nid) + return 0; + nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn) - 1); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) + return 0; + } + + /* find the primary DAC and add to the connection list */ + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int caps = get_wcaps(codec, nid); + if (get_wcaps_type(caps) == AC_WID_AUD_OUT && + !(caps & AC_WCAP_DIGITAL)) { + conn[nums++] = nid; + return snd_hda_override_conn_list(codec, + spec->aa_mix_nid, + nums, conn); + } + } + return 0; +} + + static int patch_vt1718S(struct hda_codec *codec) { struct via_spec *spec; @@ -3041,7 +3073,7 @@ static int patch_vt1718S(struct hda_codec *codec) spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); - spec->dac_mixer_idx = 5; + add_secret_dac_path(codec); /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); @@ -3402,9 +3434,9 @@ static int patch_vt2002P(struct hda_codec *codec) return -ENOMEM; spec->aa_mix_nid = 0x21; - spec->dac_mixer_idx = 3; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); + add_secret_dac_path(codec); /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); @@ -3540,7 +3572,7 @@ static int patch_vt1812(struct hda_codec *codec) spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); - spec->dac_mixer_idx = 5; + add_secret_dac_path(codec); /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); -- cgit v1.2.3 From acfa634f7e199193ec28282e82a5a6dd8edebcb7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Jul 2011 17:27:46 +0200 Subject: ALSA: hda - Add Kconfig for the default buffer size Add a Kconfig entry to specify the default buffer size. Distros using PulseAudio can choose a larger value here. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 13 +++++++++++++ sound/pci/hda/hda_intel.c | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 70762fca57ee..1f1a4ae4b791 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -14,6 +14,19 @@ menuconfig SND_HDA_INTEL if SND_HDA_INTEL +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 64 + help + Speficies the default pre-allocated buffer-size in kB for + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems with PulseAudio. The default 64 is chosen just + from the compatibility reason. + + Note that the pre-allocation size can be changed dynamically + via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. + config SND_HDA_HWDEP bool "Build hwdep interface for HD-audio driver" select SND_HWDEP diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 25619cd18831..5ce9531cba67 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2075,6 +2075,8 @@ static void azx_pcm_free(struct snd_pcm *pcm) } } +#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) + static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, struct hda_pcm *cpcm) @@ -2083,6 +2085,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, struct snd_pcm *pcm; struct azx_pcm *apcm; int pcm_dev = cpcm->device; + unsigned int size; int s, err; if (pcm_dev >= HDA_MAX_PCMS) { @@ -2118,9 +2121,12 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, snd_pcm_set_ops(pcm, s, &azx_pcm_ops); } /* buffer pre-allocation */ + size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; + if (size > MAX_PREALLOC_SIZE) + size = MAX_PREALLOC_SIZE; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), - 1024 * 64, 32 * 1024 * 1024); + size, MAX_PREALLOC_SIZE); return 0; } -- cgit v1.2.3 From cf01b73e26a8e93b46cf0b8ae878206277fb8838 Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Tue, 12 Jul 2011 19:53:56 +0200 Subject: ALSA: hda - fix up typos in Kconfig help for default buffer size introduced in acfa634f This commit is a fix up for commit acfa634f. commit acfa634f7e199193ec28282e82a5a6dd8edebcb7 Author: Takashi Iwai Date: Tue Jul 12 17:27:46 2011 +0200 ALSA: hda - Add Kconfig for the default buffer size Signed-off-by: Paul Menzel Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 1f1a4ae4b791..7489b4608551 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -19,10 +19,10 @@ config SND_HDA_PREALLOC_SIZE range 0 32768 default 64 help - Speficies the default pre-allocated buffer-size in kB for + Specifies the default pre-allocated buffer-size in kB for the HD-audio driver. A larger buffer (e.g. 2048) is preferred - for systems with PulseAudio. The default 64 is chosen just - from the compatibility reason. + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. Note that the pre-allocation size can be changed dynamically via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. -- cgit v1.2.3 From 7b1655f5f21a9bd1eb8b478c5dab9b83de809edc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Jul 2011 15:31:21 +0200 Subject: ALSA: hda - Re-add need_dac_fix check for multi-io jacks of Realtek codecs During the rewrite, the check of spec->need_dac_fix and the corresponding num_dacs change was dropped from the channel-mode control. This patch re-adds it, and also enables need_dac_fix for ALC880 as default, as this feature was originally introduced to fix h/w bugs of this chip. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 124c63f4c457..52ce07534e5b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3196,6 +3196,8 @@ static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol, for (i = 0; i < spec->multi_ios; i++) alc_set_multi_io(codec, i, i < ch); spec->multiout.max_channels = spec->ext_channel_count; + if (spec->need_dac_fix && !spec->const_channel_count) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; return 1; } @@ -3642,6 +3644,7 @@ static int patch_alc880(struct hda_codec *codec) codec->spec = spec; spec->mixer_nid = 0x0b; + spec->need_dac_fix = 1; board_config = alc_board_config(codec, ALC880_MODEL_LAST, alc880_models, alc880_cfg_tbl); -- cgit v1.2.3 From 3214b9665c06f684011f169428963b20f8ac554b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jul 2011 12:49:25 +0200 Subject: ALSA: hda - Implement dynamic loopback control for VIA codecs This patch adds the dynamic control of analog-loopback for VIA codecs. When the loopback is enabled, the inputs from line-ins and mics are mixed with the front DAC, and sent to the front outputs. The very same input is routed to the headhpones and speakers in loopback mode. However, since the loopback mix can't take other than the front DAC, there is no longer individual volume controls for headphones and speakers. Once when the loopback control is off, these volumes take effect. Since the individual volumes are more desired in general use caess, the loopback mode is set to off as default for now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 400 ++++++++++++++++++++++++++++++---------------- 1 file changed, 260 insertions(+), 140 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5232abc341f8..76c688409cd8 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -130,13 +130,28 @@ struct via_spec { struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; hda_nid_t hp_dac_nid; - bool hp_indep_shared; /* indep HP-DAC is shared with side ch */ + hda_nid_t speaker_dac_nid; + int hp_indep_shared; /* indep HP-DAC is shared with side ch */ int num_active_streams; - + int aamix_mode; /* loopback is enabled for output-path? */ + + /* Output-paths: + * There are different output-paths depending on the setup. + * out_path, hp_path and speaker_path are primary paths. If both + * direct DAC and aa-loopback routes are available, these contain + * the former paths. Meanwhile *_mix_path contain the paths with + * loopback mixer. (Since the loopback is only for front channel, + * no out_mix_path for surround channels.) + * The HP output has another path, hp_indep_path, which is used in + * the independent-HP mode. + */ struct nid_path out_path[HDA_SIDE + 1]; + struct nid_path out_mix_path; struct nid_path hp_path; - struct nid_path hp_dep_path; + struct nid_path hp_mix_path; + struct nid_path hp_indep_path; struct nid_path speaker_path; + struct nid_path speaker_mix_path; /* capture */ unsigned int num_adc_nids; @@ -437,50 +452,20 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, #define have_mute(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -static bool is_node_in_path(struct nid_path *path, hda_nid_t nid) -{ - int i; - if (!nid) - return false; - for (i = 0; i < path->depth; i++) { - if (path->path[i] == nid) - return true; - } - return false; -} - /* enable/disable the output-route mixers */ static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, - hda_nid_t mix_nid, int aa_mix_idx, bool enable) + hda_nid_t mix_nid, int idx, bool enable) { int i, num, val; - bool hp_path, front_path; - struct via_spec *spec = codec->spec; if (!path) return; num = snd_hda_get_conn_list(codec, mix_nid, NULL); - hp_path = is_node_in_path(path, spec->hp_dac_nid); - front_path = is_node_in_path(path, spec->multiout.dac_nids[0]); - for (i = 0; i < num; i++) { - if (i == aa_mix_idx) { - if (hp_path) - val = enable ? AMP_IN_MUTE(i) : - AMP_IN_UNMUTE(i); - else if (front_path) - val = AMP_IN_UNMUTE(i); - else - val = AMP_IN_MUTE(i); - } else { - if (hp_path) - val = enable ? AMP_IN_UNMUTE(i) : - AMP_IN_MUTE(i); - else if (front_path) - val = AMP_IN_MUTE(i); - else - val = AMP_IN_UNMUTE(i); - } + if (i == idx) + val = AMP_IN_UNMUTE(i); + else + val = AMP_IN_MUTE(i); snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -490,9 +475,8 @@ static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, static void activate_output_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool force) { - int i, val; struct via_spec *spec = codec->spec; - hda_nid_t aa_mix_nid = spec->aa_mix_nid; + int i; for (i = 0; i < path->depth; i++) { hda_nid_t src, dst; int idx = path->idx[i]; @@ -504,25 +488,10 @@ static void activate_output_path(struct hda_codec *codec, struct nid_path *path, if (enable && path->multi[i]) snd_hda_codec_write(codec, dst, 0, AC_VERB_SET_CONNECT_SEL, idx); - if (!force - && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT - && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX) + if (!force && (dst == spec->aa_mix_nid)) continue; - if (have_mute(codec, dst, HDA_INPUT)) { - if (dst == aa_mix_nid) { - val = enable ? AMP_IN_UNMUTE(idx) : - AMP_IN_MUTE(idx); - snd_hda_codec_write(codec, dst, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } else { - idx = get_connection_index(codec, dst, - aa_mix_nid); - if (idx >= 0) { - activate_output_mix(codec, path, - dst, idx, enable); - } - } - } + if (have_mute(codec, dst, HDA_INPUT)) + activate_output_mix(codec, path, dst, idx, enable); if (!force && (src == path->vol_ctl || src == path->mute_ctl)) continue; if (have_mute(codec, src, HDA_OUTPUT)) { @@ -548,9 +517,8 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, static void via_auto_init_output(struct hda_codec *codec, struct nid_path *path, int pin_type, - bool with_aa_mix, bool force) + bool force) { - struct via_spec *spec = codec->spec; unsigned int caps; hda_nid_t pin; @@ -566,41 +534,45 @@ static void via_auto_init_output(struct hda_codec *codec, snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE | val); } - - /* initialize the AA-path */ - if (!spec->aa_mix_nid) - return; activate_output_path(codec, path, true, force); } static void via_auto_init_multi_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + struct nid_path *path; int i; - for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) - /* enable aa-mute only for the front channel */ - via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, - i == 0, true); + for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { + path = &spec->out_path[i]; + if (!i && spec->aamix_mode && spec->out_mix_path.depth) + path = &spec->out_mix_path; + via_auto_init_output(codec, path, PIN_OUT, true); + } } static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + int shared = spec->hp_indep_shared; - if (!spec->hp_dac_nid) { - via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, - true, true); + if (!spec->hp_path.depth) { + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); return; } if (spec->hp_independent_mode) { - activate_output_path(codec, &spec->hp_dep_path, false, false); - via_auto_init_output(codec, &spec->hp_path, PIN_HP, - true, true); - } else { activate_output_path(codec, &spec->hp_path, false, false); - via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, - true, true); + activate_output_path(codec, &spec->hp_mix_path, false, false); + if (shared) + activate_output_path(codec, &spec->out_path[shared], + false, false); + via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true); + } else if (spec->aamix_mode) { + activate_output_path(codec, &spec->hp_path, false, false); + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); + } else { + activate_output_path(codec, &spec->hp_mix_path, false, false); + via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); } } @@ -608,9 +580,23 @@ static void via_auto_init_speaker_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - if (spec->autocfg.speaker_outs) + if (!spec->autocfg.speaker_outs) + return; + if (!spec->speaker_path.depth) { + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT, + true); + return; + } + if (!spec->aamix_mode) { + activate_output_path(codec, &spec->speaker_mix_path, + false, false); via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, - true, true); + true); + } else { + activate_output_path(codec, &spec->speaker_path, false, false); + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT, + true); + } } static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); @@ -775,7 +761,7 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - int cur; + int cur, shared; /* no independent-hp status change during PCM playback is running */ if (spec->num_active_streams) @@ -785,18 +771,19 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, if (spec->hp_independent_mode == cur) return 0; spec->hp_independent_mode = cur; + shared = spec->hp_indep_shared; if (cur) { - activate_output_path(codec, &spec->hp_dep_path, false, false); - activate_output_path(codec, &spec->hp_path, true, false); - if (spec->hp_indep_shared) - activate_output_path(codec, &spec->out_path[HDA_SIDE], + activate_output_path(codec, &spec->hp_mix_path, false, false); + if (shared) + activate_output_path(codec, &spec->out_path[shared], false, false); + activate_output_path(codec, &spec->hp_path, true, false); } else { activate_output_path(codec, &spec->hp_path, false, false); - activate_output_path(codec, &spec->hp_dep_path, true, false); - if (spec->hp_indep_shared) - activate_output_path(codec, &spec->out_path[HDA_SIDE], + if (shared) + activate_output_path(codec, &spec->out_path[shared], true, false); + activate_output_path(codec, &spec->hp_mix_path, true, false); } /* update jack power state */ @@ -1671,29 +1658,38 @@ static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) } static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, struct nid_path *path, - int depth, int wid_type) + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path, int depth) { + struct via_spec *spec = codec->spec; hda_nid_t conn[8]; int i, nums; + if (nid == spec->aa_mix_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ + } + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); for (i = 0; i < nums; i++) { if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) continue; - if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) - goto found; + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { + /* aa-mix is requested but not included? */ + if (!(spec->aa_mix_nid && with_aa_mix == 1)) + goto found; + } } if (depth >= MAX_NID_PATH_DEPTH) return false; for (i = 0; i < nums; i++) { unsigned int type; type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT || - (wid_type != -1 && type != wid_type)) + if (type == AC_WID_AUD_OUT) continue; if (__parse_output_path(codec, conn[i], target_dac, - path, depth + 1, AC_WID_AUD_SEL)) + with_aa_mix, path, depth + 1)) goto found; } return false; @@ -1708,11 +1704,15 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, } static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, struct nid_path *path) + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path) { - if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) { + if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { path->path[path->depth] = nid; path->depth++; + snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); return true; } return false; @@ -1728,14 +1728,24 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec) spec->multiout.dac_nids = spec->private_dac_nids; dac_num = 0; for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t dac = 0; nid = cfg->line_out_pins[i]; if (!nid) continue; - if (parse_output_path(codec, nid, 0, &spec->out_path[i])) { - spec->private_dac_nids[i] = spec->out_path[i].path[0]; + if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) + dac = spec->out_path[i].path[0]; + if (!i && parse_output_path(codec, nid, dac, 1, + &spec->out_mix_path)) + dac = spec->out_mix_path.path[0]; + if (dac) { + spec->private_dac_nids[i] = dac; dac_num++; } } + if (!spec->out_path[0].depth && spec->out_mix_path.depth) { + spec->out_path[0] = spec->out_mix_path; + spec->out_mix_path.depth = 0; + } spec->multiout.num_dacs = dac_num; return 0; } @@ -1832,6 +1842,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; + struct nid_path *path; static const char * const chname[4] = { "Front", "Surround", "C/LFE", "Side" }; @@ -1857,13 +1868,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) dac = spec->multiout.dac_nids[i]; if (!pin || !dac) continue; + path = spec->out_path + i; if (i == HDA_CLFE) { - err = create_ch_ctls(codec, "Center", 1, true, - &spec->out_path[i]); + err = create_ch_ctls(codec, "Center", 1, true, path); if (err < 0) return err; - err = create_ch_ctls(codec, "LFE", 2, true, - &spec->out_path[i]); + err = create_ch_ctls(codec, "LFE", 2, true, path); if (err < 0) return err; } else { @@ -1871,25 +1881,35 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->line_outs == 1) pfx = "Speaker"; - err = create_ch_ctls(codec, pfx, 3, true, - &spec->out_path[i]); + err = create_ch_ctls(codec, pfx, 3, true, path); if (err < 0) return err; } + if (path != spec->out_path + i) { + spec->out_path[i].vol_ctl = path->vol_ctl; + spec->out_path[i].mute_ctl = path->mute_ctl; + } + if (path == spec->out_path && spec->out_mix_path.depth) { + spec->out_mix_path.vol_ctl = path->vol_ctl; + spec->out_mix_path.mute_ctl = path->mute_ctl; + } } idx = get_connection_index(codec, spec->aa_mix_nid, spec->multiout.dac_nids[0]); if (idx >= 0) { /* add control to mixer */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "PCM Playback Volume", + const char *name; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Volume" : "PCM Playback Volume"; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "PCM Playback Switch", + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Switch" : "PCM Playback Switch"; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, idx, HDA_INPUT)); if (err < 0) @@ -1906,70 +1926,167 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) struct via_spec *spec = codec->spec; struct nid_path *path; bool check_dac; - int err; + int i, err; if (!pin) return 0; - if (parse_output_path(codec, pin, 0, &spec->hp_path)) - spec->hp_dac_nid = spec->hp_path.path[0]; - else if (spec->multiout.dac_nids[HDA_SIDE] && - parse_output_path(codec, pin, - spec->multiout.dac_nids[HDA_SIDE], - &spec->hp_path)) { - spec->hp_dac_nid = spec->hp_path.path[0]; - spec->hp_indep_shared = true; - } else if (spec->multiout.dac_nids[HDA_CLFE] && - parse_output_path(codec, pin, - spec->multiout.dac_nids[HDA_CLFE], - &spec->hp_path)) { - spec->hp_dac_nid = spec->hp_path.path[0]; - spec->hp_indep_shared = true; + if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { + for (i = HDA_SIDE; i >= HDA_CLFE; i--) { + if (i < spec->multiout.num_dacs && + parse_output_path(codec, pin, + spec->multiout.dac_nids[i], 0, + &spec->hp_indep_path)) { + spec->hp_indep_shared = i; + break; + } + } } + if (spec->hp_indep_path.depth) { + spec->hp_dac_nid = spec->hp_indep_path.path[0]; + if (!spec->hp_indep_shared) + spec->hp_path = spec->hp_indep_path; + } + /* optionally check front-path w/o AA-mix */ + if (!spec->hp_path.depth) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->hp_path); if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - &spec->hp_dep_path) && - !spec->hp_dac_nid) + 1, &spec->hp_mix_path) && !spec->hp_path.depth) return 0; - if (spec->hp_dac_nid && !spec->hp_indep_shared) { + if (spec->hp_path.depth) { path = &spec->hp_path; check_dac = true; } else { - path = &spec->hp_dep_path; + path = &spec->hp_mix_path; check_dac = false; } err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); if (err < 0) return err; - if (spec->hp_dac_nid) { - spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl; - spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl; + if (check_dac) { + spec->hp_mix_path.vol_ctl = path->vol_ctl; + spec->hp_mix_path.mute_ctl = path->mute_ctl; + } else { + spec->hp_path.vol_ctl = path->vol_ctl; + spec->hp_path.mute_ctl = path->mute_ctl; } - return 0; } static int via_auto_create_speaker_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + struct nid_path *path; + bool check_dac; hda_nid_t pin, dac; + int err; pin = spec->autocfg.speaker_pins[0]; if (!spec->autocfg.speaker_outs || !pin) return 0; - if (parse_output_path(codec, pin, 0, &spec->speaker_path)) { + if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) dac = spec->speaker_path.path[0]; - spec->multiout.extra_out_nid[0] = dac; - return create_ch_ctls(codec, "Speaker", 3, true, - &spec->speaker_path); + if (!dac) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->speaker_path); + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->speaker_mix_path) && !dac) + return 0; + + /* no AA-path for front? */ + if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) + dac = 0; + + spec->speaker_dac_nid = dac; + spec->multiout.extra_out_nid[0] = dac; + if (dac) { + path = &spec->speaker_path; + check_dac = true; + } else { + path = &spec->speaker_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); + if (err < 0) + return err; + if (check_dac) { + spec->speaker_mix_path.vol_ctl = path->vol_ctl; + spec->speaker_mix_path.mute_ctl = path->mute_ctl; + } else { + spec->speaker_path.vol_ctl = path->vol_ctl; + spec->speaker_path.mute_ctl = path->mute_ctl; } - if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - &spec->speaker_path)) - return create_ch_ctls(codec, "Speaker", 3, false, - &spec->speaker_path); + return 0; +} + +#define via_aamix_ctl_info via_pin_power_ctl_info +static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->aamix_mode; + return 0; +} + +static void update_aamix_paths(struct hda_codec *codec, int do_mix, + struct nid_path *nomix, struct nid_path *mix) +{ + if (do_mix) { + activate_output_path(codec, nomix, false, false); + activate_output_path(codec, mix, true, false); + } else { + activate_output_path(codec, mix, false, false); + activate_output_path(codec, nomix, true, false); + } +} + +static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val == spec->aamix_mode) + return 0; + spec->aamix_mode = val; + /* update front path */ + update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); + /* update HP path */ + if (!spec->hp_independent_mode) { + update_aamix_paths(codec, val, &spec->hp_path, + &spec->hp_mix_path); + } + /* update speaker path */ + update_aamix_paths(codec, val, &spec->speaker_path, + &spec->speaker_mix_path); + return 1; +} + +static const struct snd_kcontrol_new via_aamix_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Loopback Mixing", + .info = via_aamix_ctl_info, + .get = via_aamix_ctl_get, + .put = via_aamix_ctl_put, +}; + +static int via_auto_create_loopback_switch(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->aa_mix_nid || !spec->out_mix_path.depth) + return 0; /* no loopback switching available */ + if (!via_clone_control(spec, &via_aamix_ctl_enum)) + return -ENOMEM; return 0; } @@ -2438,6 +2555,9 @@ static int via_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; err = via_auto_create_speaker_ctls(codec); + if (err < 0) + return err; + err = via_auto_create_loopback_switch(codec); if (err < 0) return err; err = via_auto_create_analog_input_ctls(codec); @@ -2453,7 +2573,7 @@ static int via_parse_auto_config(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = spec->kctls.list; - if (spec->hp_dac_nid && spec->hp_dep_path.depth) { + if (spec->hp_dac_nid && spec->hp_mix_path.depth) { err = via_hp_build(codec); if (err < 0) return err; -- cgit v1.2.3 From 3b607e3d3a2538e06686c8c26057f95471ac1f9c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jul 2011 16:54:40 +0200 Subject: ALSA: hda - Switch HP DAC dynamically with indep-HP switch for VIA This patch changes the behavior of independent-HP enum switch. Now instead of returning a busy error, the driver switches dynamically the stream of the HP (and shared) DACs according to the current mode. The logic is similar like the dual-mic ADC switch, but a bit more complicated because of the presence of shared DAC. Together with the change, a mutex is introduced to protect against the possible races for the indep-HP mode setting. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 159 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 125 insertions(+), 34 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 76c688409cd8..5b0342635ebe 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -109,6 +109,11 @@ struct via_input { #define VIA_MAX_ADCS 3 +enum { + STREAM_MULTI_OUT = (1 << 0), + STREAM_INDEP_HP = (1 << 1), +}; + struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; @@ -132,7 +137,8 @@ struct via_spec { hda_nid_t hp_dac_nid; hda_nid_t speaker_dac_nid; int hp_indep_shared; /* indep HP-DAC is shared with side ch */ - int num_active_streams; + int opened_streams; /* STREAM_* bits */ + int active_streams; /* STREAM_* bits */ int aamix_mode; /* loopback is enabled for output-path? */ /* Output-paths: @@ -166,6 +172,12 @@ struct via_spec { struct via_input inputs[AUTO_CFG_MAX_INS + 1]; unsigned int cur_mux[VIA_MAX_ADCS]; + /* dynamic DAC switching */ + unsigned int cur_dac_stream_tag; + unsigned int cur_dac_format; + unsigned int cur_hp_stream_tag; + unsigned int cur_hp_format; + /* dynamic ADC switching */ hda_nid_t cur_adc; unsigned int cur_adc_stream_tag; @@ -207,6 +219,8 @@ struct via_spec { /* bind capture-volume */ struct hda_bind_ctls *bind_cap_vol; struct hda_bind_ctls *bind_cap_sw; + + struct mutex config_mutex; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -218,6 +232,7 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) if (spec == NULL) return NULL; + mutex_init(&spec->config_mutex); codec->spec = spec; spec->codec = codec; spec->codec_type = get_codec_type(codec); @@ -756,6 +771,67 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, return 0; } +/* adjust spec->multiout setup according to the current flags */ +static void setup_playback_multi_pcm(struct via_spec *spec) +{ + const struct auto_pin_cfg *cfg = &spec->autocfg; + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.hp_nid = 0; + if (!spec->hp_independent_mode) { + if (!spec->hp_indep_shared) + spec->multiout.hp_nid = spec->hp_dac_nid; + } else { + if (spec->hp_indep_shared) + spec->multiout.num_dacs = cfg->line_outs - 1; + } +} + +/* update DAC setups according to indep-HP switch; + * this function is called only when indep-HP is modified + */ +static void switch_indep_hp_dacs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int shared = spec->hp_indep_shared; + hda_nid_t shared_dac, hp_dac; + + if (!spec->opened_streams) + return; + + shared_dac = shared ? spec->multiout.dac_nids[shared] : 0; + hp_dac = spec->hp_dac_nid; + if (spec->hp_independent_mode) { + /* switch to indep-HP mode */ + if (spec->active_streams & STREAM_MULTI_OUT) { + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + __snd_hda_codec_cleanup_stream(codec, shared_dac, 1); + } + if (spec->active_streams & STREAM_INDEP_HP) + snd_hda_codec_setup_stream(codec, hp_dac, + spec->cur_hp_stream_tag, 0, + spec->cur_hp_format); + } else { + /* back to HP or shared-DAC */ + if (spec->active_streams & STREAM_INDEP_HP) + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + if (spec->active_streams & STREAM_MULTI_OUT) { + hda_nid_t dac; + int ch; + if (shared_dac) { /* reset mutli-ch DAC */ + dac = shared_dac; + ch = shared * 2; + } else { /* reset HP DAC */ + dac = hp_dac; + ch = 0; + } + snd_hda_codec_setup_stream(codec, dac, + spec->cur_dac_stream_tag, ch, + spec->cur_dac_format); + } + } + setup_playback_multi_pcm(spec); +} + static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -763,13 +839,12 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct via_spec *spec = codec->spec; int cur, shared; - /* no independent-hp status change during PCM playback is running */ - if (spec->num_active_streams) - return -EBUSY; - + mutex_lock(&spec->config_mutex); cur = !!ucontrol->value.enumerated.item[0]; - if (spec->hp_independent_mode == cur) + if (spec->hp_independent_mode == cur) { + mutex_unlock(&spec->config_mutex); return 0; + } spec->hp_independent_mode = cur; shared = spec->hp_indep_shared; if (cur) { @@ -786,6 +861,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, activate_output_path(codec, &spec->hp_mix_path, true, false); } + switch_indep_hp_dacs(codec); + mutex_unlock(&spec->config_mutex); + /* update jack power state */ set_widgets_power_state(codec); via_hp_automute(codec); @@ -948,7 +1026,7 @@ static void analog_low_current_mode(struct hda_codec *codec) bool enable; unsigned int verb, parm; - enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0); + enable = is_aa_path_mute(codec) && (spec->opened_streams != 0); /* decide low current mode's verb & parameter */ switch (spec->codec_type) { @@ -989,14 +1067,14 @@ static const struct hda_verb vt1708_init_verbs[] = { { } }; -static void set_stream_active(struct hda_codec *codec, bool active) +static void set_stream_open(struct hda_codec *codec, int bit, bool active) { struct via_spec *spec = codec->spec; if (active) - spec->num_active_streams++; + spec->opened_streams |= bit; else - spec->num_active_streams--; + spec->opened_streams &= ~bit; analog_low_current_mode(codec); } @@ -1008,22 +1086,13 @@ static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, const struct auto_pin_cfg *cfg = &spec->autocfg; int err; - spec->multiout.hp_nid = 0; spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; - if (!spec->hp_independent_mode) { - if (!spec->hp_indep_shared) - spec->multiout.hp_nid = spec->hp_dac_nid; - } else { - if (spec->hp_indep_shared) - spec->multiout.num_dacs = cfg->line_outs - 1; - } spec->multiout.max_channels = spec->multiout.num_dacs * 2; - set_stream_active(codec, true); + set_stream_open(codec, STREAM_MULTI_OUT, true); err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); if (err < 0) { - spec->multiout.hp_nid = 0; - set_stream_active(codec, false); + set_stream_open(codec, STREAM_MULTI_OUT, false); return err; } return 0; @@ -1033,10 +1102,7 @@ static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct via_spec *spec = codec->spec; - - spec->multiout.hp_nid = 0; - set_stream_active(codec, false); + set_stream_open(codec, STREAM_MULTI_OUT, false); return 0; } @@ -1048,9 +1114,7 @@ static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, if (snd_BUG_ON(!spec->hp_dac_nid)) return -EINVAL; - if (!spec->hp_independent_mode || spec->multiout.hp_nid) - return -EBUSY; - set_stream_active(codec, true); + set_stream_open(codec, STREAM_INDEP_HP, true); return 0; } @@ -1058,7 +1122,7 @@ static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - set_stream_active(codec, false); + set_stream_open(codec, STREAM_INDEP_HP, false); return 0; } @@ -1070,8 +1134,15 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, { struct via_spec *spec = codec->spec; + mutex_lock(&spec->config_mutex); + setup_playback_multi_pcm(spec); snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); + /* remember for dynamic DAC switch with indep-HP */ + spec->active_streams |= STREAM_MULTI_OUT; + spec->cur_dac_stream_tag = stream_tag; + spec->cur_dac_format = format; + mutex_unlock(&spec->config_mutex); vt1708_start_hp_work(spec); return 0; } @@ -1084,8 +1155,14 @@ static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, { struct via_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, - stream_tag, 0, format); + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, + stream_tag, 0, format); + spec->active_streams |= STREAM_INDEP_HP; + spec->cur_hp_stream_tag = stream_tag; + spec->cur_hp_format = format; + mutex_unlock(&spec->config_mutex); vt1708_start_hp_work(spec); return 0; } @@ -1096,7 +1173,10 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct via_spec *spec = codec->spec; + mutex_lock(&spec->config_mutex); snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + spec->active_streams &= ~STREAM_MULTI_OUT; + mutex_unlock(&spec->config_mutex); vt1708_stop_hp_work(spec); return 0; } @@ -1107,7 +1187,11 @@ static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct via_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); + spec->active_streams &= ~STREAM_INDEP_HP; + mutex_unlock(&spec->config_mutex); vt1708_stop_hp_work(spec); return 0; } @@ -1186,10 +1270,12 @@ static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct via_spec *spec = codec->spec; int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; + mutex_lock(&spec->config_mutex); spec->cur_adc = spec->adc_nids[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); + mutex_unlock(&spec->config_mutex); return 0; } @@ -1199,8 +1285,10 @@ static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct via_spec *spec = codec->spec; + mutex_lock(&spec->config_mutex); snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = 0; + mutex_unlock(&spec->config_mutex); return 0; } @@ -1210,7 +1298,9 @@ static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) struct via_spec *spec = codec->spec; int adc_idx = spec->inputs[cur].adc_idx; hda_nid_t adc = spec->adc_nids[adc_idx]; + bool ret = false; + mutex_lock(&spec->config_mutex); if (spec->cur_adc && spec->cur_adc != adc) { /* stream is running, let's swap the current ADC */ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); @@ -1218,9 +1308,10 @@ static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) snd_hda_codec_setup_stream(codec, adc, spec->cur_adc_stream_tag, 0, spec->cur_adc_format); - return true; + ret = true; } - return false; + mutex_unlock(&spec->config_mutex); + return ret; } static const struct hda_pcm_stream via_pcm_analog_playback = { -- cgit v1.2.3 From 737c265bb8399b97231e5b10b5b2375914206427 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Jul 2011 09:34:10 +0200 Subject: ALSA: hda - Add documentation for codec-specific mixer controls Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Controls.txt | 100 +++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Documentation/sound/alsa/HD-Audio-Controls.txt diff --git a/Documentation/sound/alsa/HD-Audio-Controls.txt b/Documentation/sound/alsa/HD-Audio-Controls.txt new file mode 100644 index 000000000000..1482035243e6 --- /dev/null +++ b/Documentation/sound/alsa/HD-Audio-Controls.txt @@ -0,0 +1,100 @@ +This file explains the codec-specific mixer controls. + +Realtek codecs +-------------- + +* Channel Mode + This is an enum control to change the surround-channel setup, + appears only when the surround channels are available. + It gives the number of channels to be used, "2ch", "4ch", "6ch", + and "8ch". According to the configuration, this also controls the + jack-retasking of multi-I/O jacks. + +* Auto-Mute Mode + This is an enum control to change the auto-mute behavior of the + headphone and line-out jacks. If built-in speakers and headphone + and/or line-out jacks are available on a machine, this controls + appears. + When there are only either headphones or line-out jacks, it gives + "Disabled" and "Enabled" state. When enabled, the speaker is muted + automatically when a jack is plugged. + + When both headphone and line-out jacks are present, it gives + "Disabled", "Speaker Only" and "Line-Out+Speaker". When + speaker-only is chosen, plugging into a headphone or a line-out jack + mutes the speakers, but not line-outs. When line-out+speaker is + selected, plugging to a headphone jack mutes both speakers and + line-outs. + + +IDT/Sigmatel codecs +------------------- + +* Analog Loopback + This control enables/disables the analog-loopback circuit. This + appears only when "loopback" is set to true in a codec hint + (see HD-Audio.txt). Note that on some codecs the analog-loopback + and the normal PCM playback are exclusive, i.e. when this is on, you + won't hear any PCM stream. + +* Swap Center/LFE + Swaps the center and LFE channel order. Normally, the left + corresponds to the center and the right to the LFE. When this is + ON, the left to the LFE and the right to the center. + +* Headphone as Line Out + When this control is ON, treat the headphone jacks as line-out + jacks. That is, the headphone won't auto-mute the other line-outs, + and no HP-amp is set to the pins. + +* Mic Jack Mode, Line Jack Mode, etc + These enum controls the direction and the bias of the input jack + pins. Depending on the jack type, it can set as "Mic In" and "Line + In", for determining the input bias, or it can be set to "Line Out" + when the pin is a multi-I/O jack for surround channels. + + +VIA codecs +---------- + +* Smart 5.1 + An enum control to re-task the multi-I/O jacks for surround outputs. + When it's ON, the corresponding input jacks (usually a line-in and a + mic-in) are switched as the surround and the CLFE output jacks. + +* Independent HP + When this enum control is enabled, the headphone output is routed + from an individual stream (the third PCM such as hw:0,2) instead of + the primary stream. In the case the headphone DAC is shared with a + side or a CLFE-channel DAC, the DAC is switched to the headphone + automatically. + +* Loopback Mixing + An enum control to determine whether the analog-loopback route is + enabled or not. When it's enabled, the analog-loopback is mixed to + the front-channel. Also, the same route is used for the headphone + and speaker outputs. As a side-effect, when this mode is set, the + individual volume controls will be no longer available for + headphones and speakers because there is only one DAC connected to a + mixer widget. + +* Dynamic Power-Control + This control determines whether the dynamic power-control per jack + detection is enabled or not. When enabled, the widgets power state + (D0/D3) are changed dynamically depending on the jack plugging + state for saving power consumptions. However, if your system + doesn't provide a proper jack-detection, this won't work; in such a + case, turn this control OFF. + +* Jack Detect + This control is provided only for VT1708 codec which gives no proper + unsolicited event per jack plug. When this is on, the driver polls + the jack detection so that the headphone auto-mute can work, while + turning this off would reduce the power consumption. + + +Conexant codecs +--------------- + +* Auto-Mute Mode + See Reatek codecs. -- cgit v1.2.3 From 020066d1ecc95d74da9be6beb436ac575af01271 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 21 Jul 2011 13:45:56 +0200 Subject: ALSA: hda - Fix indep-HP path (de-)activation for VT1708* codecs This patch fixes non-working indep-HP control on VT1708* codecs. The problems are that via_independent_hp_put() wasn't fixed to follow the recent change of three HP paths, and hp_indep_path didn't contain the amp nids of mixer elements. Together with the fixes, a few code clean-ups are done. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 92 ++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5b0342635ebe..761339a0694d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -566,31 +566,44 @@ static void via_auto_init_multi_out(struct hda_codec *codec) } } -static void via_auto_init_hp_out(struct hda_codec *codec) +/* deactivate the inactive headphone-paths */ +static void deactivate_hp_paths(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int shared = spec->hp_indep_shared; - if (!spec->hp_path.depth) { - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); - return; - } if (spec->hp_independent_mode) { activate_output_path(codec, &spec->hp_path, false, false); activate_output_path(codec, &spec->hp_mix_path, false, false); if (shared) activate_output_path(codec, &spec->out_path[shared], false, false); - via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true); - } else if (spec->aamix_mode) { + } else if (spec->aamix_mode || !spec->hp_path.depth) { + activate_output_path(codec, &spec->hp_indep_path, false, false); activate_output_path(codec, &spec->hp_path, false, false); - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); } else { + activate_output_path(codec, &spec->hp_indep_path, false, false); activate_output_path(codec, &spec->hp_mix_path, false, false); - via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); } } +static void via_auto_init_hp_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->hp_path.depth) { + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); + return; + } + deactivate_hp_paths(codec); + if (spec->hp_independent_mode) + via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true); + else if (spec->aamix_mode) + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); + else + via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); +} + static void via_auto_init_speaker_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -847,18 +860,19 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, } spec->hp_independent_mode = cur; shared = spec->hp_indep_shared; - if (cur) { - activate_output_path(codec, &spec->hp_mix_path, false, false); - if (shared) - activate_output_path(codec, &spec->out_path[shared], - false, false); - activate_output_path(codec, &spec->hp_path, true, false); - } else { - activate_output_path(codec, &spec->hp_path, false, false); + deactivate_hp_paths(codec); + if (cur) + activate_output_path(codec, &spec->hp_indep_path, true, false); + else { if (shared) activate_output_path(codec, &spec->out_path[shared], true, false); - activate_output_path(codec, &spec->hp_mix_path, true, false); + if (spec->aamix_mode || !spec->hp_path.depth) + activate_output_path(codec, &spec->hp_mix_path, + true, false); + else + activate_output_path(codec, &spec->hp_path, + true, false); } switch_indep_hp_dacs(codec); @@ -1928,6 +1942,12 @@ static void mangle_smart51(struct hda_codec *codec) } } +static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src) +{ + dst->vol_ctl = src->vol_ctl; + dst->mute_ctl = src->mute_ctl; +} + /* add playback controls from the parsed DAC table */ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) { @@ -1976,14 +1996,10 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (err < 0) return err; } - if (path != spec->out_path + i) { - spec->out_path[i].vol_ctl = path->vol_ctl; - spec->out_path[i].mute_ctl = path->mute_ctl; - } - if (path == spec->out_path && spec->out_mix_path.depth) { - spec->out_mix_path.vol_ctl = path->vol_ctl; - spec->out_mix_path.mute_ctl = path->mute_ctl; - } + if (path != spec->out_path + i) + copy_path_mixer_ctls(&spec->out_path[i], path); + if (path == spec->out_path && spec->out_mix_path.depth) + copy_path_mixer_ctls(&spec->out_mix_path, path); } idx = get_connection_index(codec, spec->aa_mix_nid, @@ -2058,13 +2074,12 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); if (err < 0) return err; - if (check_dac) { - spec->hp_mix_path.vol_ctl = path->vol_ctl; - spec->hp_mix_path.mute_ctl = path->mute_ctl; - } else { - spec->hp_path.vol_ctl = path->vol_ctl; - spec->hp_path.mute_ctl = path->mute_ctl; - } + if (check_dac) + copy_path_mixer_ctls(&spec->hp_mix_path, path); + else + copy_path_mixer_ctls(&spec->hp_path, path); + if (spec->hp_indep_path.depth) + copy_path_mixer_ctls(&spec->hp_indep_path, path); return 0; } @@ -2106,13 +2121,10 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec) err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); if (err < 0) return err; - if (check_dac) { - spec->speaker_mix_path.vol_ctl = path->vol_ctl; - spec->speaker_mix_path.mute_ctl = path->mute_ctl; - } else { - spec->speaker_path.vol_ctl = path->vol_ctl; - spec->speaker_path.mute_ctl = path->mute_ctl; - } + if (check_dac) + copy_path_mixer_ctls(&spec->speaker_mix_path, path); + else + copy_path_mixer_ctls(&spec->speaker_path, path); return 0; } -- cgit v1.2.3 From a353fbb17961780c13e585e8658006ef0e543733 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 21 Jul 2011 14:23:35 +0200 Subject: ALSA: hda - Remove a superfluous argument of via_auto_init_output() "force" argument is always true, so let's strip it off. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 761339a0694d..f38160b00e16 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -531,8 +531,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, } static void via_auto_init_output(struct hda_codec *codec, - struct nid_path *path, int pin_type, - bool force) + struct nid_path *path, int pin_type) { unsigned int caps; hda_nid_t pin; @@ -549,7 +548,7 @@ static void via_auto_init_output(struct hda_codec *codec, snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE | val); } - activate_output_path(codec, path, true, force); + activate_output_path(codec, path, true, true); /* force on */ } static void via_auto_init_multi_out(struct hda_codec *codec) @@ -562,7 +561,7 @@ static void via_auto_init_multi_out(struct hda_codec *codec) path = &spec->out_path[i]; if (!i && spec->aamix_mode && spec->out_mix_path.depth) path = &spec->out_mix_path; - via_auto_init_output(codec, path, PIN_OUT, true); + via_auto_init_output(codec, path, PIN_OUT); } } @@ -592,16 +591,16 @@ static void via_auto_init_hp_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (!spec->hp_path.depth) { - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); return; } deactivate_hp_paths(codec); if (spec->hp_independent_mode) - via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP); else if (spec->aamix_mode) - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); else - via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); + via_auto_init_output(codec, &spec->hp_path, PIN_HP); } static void via_auto_init_speaker_out(struct hda_codec *codec) @@ -611,19 +610,16 @@ static void via_auto_init_speaker_out(struct hda_codec *codec) if (!spec->autocfg.speaker_outs) return; if (!spec->speaker_path.depth) { - via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT, - true); + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); return; } if (!spec->aamix_mode) { activate_output_path(codec, &spec->speaker_mix_path, false, false); - via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, - true); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT); } else { activate_output_path(codec, &spec->speaker_path, false, false); - via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT, - true); + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); } } -- cgit v1.2.3