From 52fd5cbc9bef6a2e20bfbdae771498ef97c67b34 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 08:45:33 +0100 Subject: [PATCH 1/9] ALSA: hda - Check pincap while parsing the configuration Sometimes (or rather often) BIOS sets the pin default configurations obviously wrongly. Looking through these failures, one common pattern is to enable some dead pins that are usually marked as speaker pins. In such a case, we can skip them if the pins don't have the output capability. In this patch, add a check for the valid pin cap bit for each parsed pin, and filter out when it's invalid. The fix was originally suggested by Raymond Yau. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 33b3ece224c6..a4810c7437bd 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -97,6 +97,28 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) } } +/* check whether the given pin has a proper pin I/O capability bit */ +static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, + unsigned int dev) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, pin); + + /* some old hardware don't return the proper pincaps */ + if (!pincap) + return true; + + switch (dev) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + case AC_JACK_HP_OUT: + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return !!(pincap & AC_PINCAP_OUT); + default: + return !!(pincap & AC_PINCAP_IN); + } +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -164,6 +186,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, dev = AC_JACK_SPEAKER; } + if (!check_pincap_validity(codec, nid, dev)) + continue; + switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); From f038fcaca827a2330d502a5d653ab639419f45db Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 15 Jan 2013 15:27:19 +0100 Subject: [PATCH 2/9] ALSA: hda - fix OOPS in hda_mark_cmd_cache_dirty Obvious copy-paste error. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b28e4031b8a1..e6cdad713734 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3794,7 +3794,7 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) } for (i = 0; i < codec->amp_cache.buf.used; i++) { struct hda_amp_info *amp; - amp = snd_array_elem(&codec->cmd_cache.buf, i); + amp = snd_array_elem(&codec->amp_cache.buf, i); amp->head.dirty = 1; } } From ea46c3c87c35b90139b4dca43917d0f605d568ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 18:45:53 +0100 Subject: [PATCH 3/9] ALSA: hda - Add prefer_hp_amp flag to hda_gen_spec Add a new flag to indicate whether HP amp is turned on as default for speaker or line-outs, and enable this for ALC260 codec, as many machines with this codec require the HP amp even for speakers. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 14 ++++++++++---- sound/pci/hda/hda_generic.h | 1 + sound/pci/hda/patch_realtek.c | 7 +++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 932e6a133f3d..e878a9effc96 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1284,6 +1284,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i, err, badness; + unsigned int val; /* set num_dacs once to full for look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; @@ -1421,13 +1422,18 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->speaker_paths); /* set initial pinctl targets */ - set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, - cfg->line_out_type == AUTO_PIN_HP_OUT ? PIN_HP : PIN_OUT); + if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) + val = PIN_HP; + else + val = PIN_OUT; + set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); if (cfg->line_out_type != AUTO_PIN_HP_OUT) set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; set_pin_targets(codec, cfg->speaker_outs, - cfg->speaker_pins, PIN_OUT); + cfg->speaker_pins, val); + } return badness; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1ceaacd1b775..6ba580540081 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -190,6 +190,7 @@ struct hda_gen_spec { unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ unsigned int indep_hp:1; /* independent HP supported */ unsigned int indep_hp_enabled:1; /* independent HP enabled */ + unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fab31d29fa23..71a8894438ab 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1506,8 +1506,6 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->gen.add_out_jack_modes = 1; - else if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_set_pin_ctl_cache(codec, 0x10, PIN_HP); } static const struct hda_fixup alc260_fixups[] = { @@ -1597,6 +1595,11 @@ static int patch_alc260(struct hda_codec *codec) return err; spec = codec->spec; + /* as quite a few machines require HP amp for speaker outputs, + * it's easier to enable it unconditionally; even if it's unneeded, + * it's almost harmless. + */ + spec->gen.prefer_hp_amp = 1; snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); From b56fa1ed09615f148271045d220b1c55580bdfc9 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 11:45:35 +0100 Subject: [PATCH 4/9] ALSA: hda - Check array bounds in get_input_path This gives us some additional safety. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e878a9effc96..1fa71acc348d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2403,8 +2403,16 @@ static int create_input_ctls(struct hda_codec *codec) static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) { struct hda_gen_spec *spec = codec->spec; + if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { + snd_BUG(); + return NULL; + } if (spec->dyn_adc_switch) adc_idx = spec->dyn_adc_idx[imux_idx]; + if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_OUTS) { + snd_BUG(); + return NULL; + } return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); } From a053d1e3c43e39109e640d1516669aeb8ce0b60b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 11:45:36 +0100 Subject: [PATCH 5/9] ALSA: hda - fix wrong adc_idx in generic parser We use knew->index for adc_idx when we create "Capture Volume" and "Capture Switch", so use the same to retrieve adc_idx. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fa71acc348d..68947fa23400 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2432,7 +2432,7 @@ static int mux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + unsigned int adc_idx = kcontrol->id.index; ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; return 0; @@ -2442,7 +2442,7 @@ static int 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); + unsigned int adc_idx = kcontrol->id.index; return mux_select(codec, adc_idx, ucontrol->value.enumerated.item[0]); } @@ -2474,7 +2474,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, int i, adc_idx, err = 0; imux = &spec->input_mux; - adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + adc_idx = kcontrol->id.index; mutex_lock(&codec->control_mutex); /* we use the cache-only update at first since multiple input paths * may shared the same amp; by updating only caches, the redundant From c0f3b21643487e2bbf8af534a33ffd2857e18fa1 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 11:45:37 +0100 Subject: [PATCH 6/9] ALSA: hda - initialize channel counts correctly Even a single DAC can output two channels, so the channel count is twice the number of DACs. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 68947fa23400..c33e019617cd 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1400,7 +1400,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, } spec->ext_channel_count = spec->min_channel_count = - spec->multiout.num_dacs; + spec->multiout.num_dacs * 2; if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) From 02aba550537a666b8d09346f39d6372c78b115a5 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 15:58:43 +0100 Subject: [PATCH 7/9] ALSA: hda - do not add non-existing Mic boost controls If the input node does not have any volume capable input amp, don't add such a control. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c33e019617cd..05dfeb7bfc97 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2826,6 +2826,9 @@ static int parse_mic_boost(struct hda_codec *codec) struct nid_path *path; unsigned int val; + if (!nid_has_volume(codec, nid, HDA_INPUT)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); if (prev_label && !strcmp(label, prev_label)) type_idx++; From 99a5592d6a897eed447df1fac6b591c06c891858 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 15:58:44 +0100 Subject: [PATCH 8/9] ALSA: hda - force different capture controls if amp caps differ Otherwise setting the capture volume for amps will be weird and inconsistent (it will try to set values outside the range of the second amp based on capabilities of the first amp). Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 05dfeb7bfc97..171364a9dda4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -484,6 +484,15 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, return false; } +static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, + hda_nid_t nid2, int dir) +{ + if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) + return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); + return (query_amp_caps(codec, nid1, dir) == + query_amp_caps(codec, nid2, dir)); +} + #define nid_has_mute(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) #define nid_has_volume(codec, nid, dir) \ @@ -2768,6 +2777,7 @@ static int create_capture_mixers(struct hda_codec *codec) for (n = 0; n < nums; n++) { bool multi = false; + bool multi_cap_vol = spec->multi_cap_vol; bool inv_dmic = false; int vol, sw; @@ -2780,12 +2790,20 @@ static int create_capture_mixers(struct hda_codec *codec) parse_capvol_in_path(codec, path); if (!vol) vol = path->ctls[NID_PATH_VOL_CTL]; - else if (vol != path->ctls[NID_PATH_VOL_CTL]) + else if (vol != path->ctls[NID_PATH_VOL_CTL]) { multi = true; + if (!same_amp_caps(codec, vol, + path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) + multi_cap_vol = true; + } if (!sw) sw = path->ctls[NID_PATH_MUTE_CTL]; - else if (sw != path->ctls[NID_PATH_MUTE_CTL]) + else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { multi = true; + if (!same_amp_caps(codec, sw, + path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) + multi_cap_vol = true; + } if (is_inv_dmic_pin(codec, spec->imux_pins[i])) inv_dmic = true; } @@ -2793,7 +2811,7 @@ static int create_capture_mixers(struct hda_codec *codec) if (!multi) err = create_single_cap_vol_ctl(codec, n, vol, sw, inv_dmic); - else if (!spec->multi_cap_vol) + else if (!multi_cap_vol) err = create_bind_cap_vol_ctl(codec, n, vol, sw); else err = create_multi_cap_vol_ctl(codec); From 6fc4cb97cbf5bf1ccfac821072e3927d21b3e0e9 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 15:58:45 +0100 Subject: [PATCH 9/9] ALSA: hda - Make sure fill_all_dac_nids is called for digital only codecs Otherwise no PCM will be built for codecs without analog I/O. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 171364a9dda4..73900d935681 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1516,8 +1516,6 @@ static int parse_output_paths(struct hda_codec *codec) bool best_wired = true, best_mio = true; bool hp_spk_swapped = false; - fill_all_dac_nids(codec); - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); if (!best_cfg) return -ENOMEM; @@ -3428,6 +3426,8 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, cfg = &spec->autocfg; } + fill_all_dac_nids(codec); + if (!cfg->line_outs) { if (cfg->dig_outs || cfg->dig_in_pin) { spec->multiout.max_channels = 2;