summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-11-12 09:48:04 +0100
committerTakashi Iwai <tiwai@suse.de>2008-11-12 10:00:56 +0100
commitd7a8943635485597ae7c6d554a8ccf3ce5a42d2d (patch)
treeceb41ca8ba7f143d2c9d63dec0fe60abc7d59a20 /sound
parent4f1e6bc3646ab50b8181555ab7e6eeab68b8632a (diff)
downloadlinux-3.10-d7a8943635485597ae7c6d554a8ccf3ce5a42d2d.tar.gz
linux-3.10-d7a8943635485597ae7c6d554a8ccf3ce5a42d2d.tar.bz2
linux-3.10-d7a8943635485597ae7c6d554a8ccf3ce5a42d2d.zip
ALSA: hda - Fix IDT/STAC multiple HP detection
Due to the recent change for multiple HP as line-out switch, only one of the multiple headphons (usually a wrong one) is toggled and the other pins are still disabled. This causes the silent output problem on some Dell laptops. Also, the hp_switch check is screwed up when a line-in or a mic-in jack exists. This is added as an additional output, but hp_switch check doesn't take it into account. This patch fixes these issues: simplify hp_switch check by using the NID instead of bool, and clean up / fix the toggle of HP pins in unsol event handler code. Reference: Novell bnc#443267 https://bugzilla.novell.com/show_bug.cgi?id=443267 Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/patch_sigmatel.c59
1 files changed, 45 insertions, 14 deletions
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index a51160106df..4300a679cd8 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -212,7 +212,7 @@ struct sigmatel_spec {
/* i/o switches */
unsigned int io_switch[2];
unsigned int clfe_swap;
- unsigned int hp_switch;
+ unsigned int hp_switch; /* NID of HP as line-out */
unsigned int aloopback;
struct hda_pcm pcm_rec[2]; /* PCM information */
@@ -2443,7 +2443,7 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = spec->hp_switch;
+ ucontrol->value.integer.value[0] = !!spec->hp_switch;
return 0;
}
@@ -2452,8 +2452,9 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
-
- spec->hp_switch = ucontrol->value.integer.value[0];
+ int nid = kcontrol->private_value;
+
+ spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0;
/* check to be sure that the ports are upto date with
* switch changes
@@ -2862,7 +2863,8 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
if (cfg->hp_outs > 1) {
err = stac92xx_add_control(spec,
STAC_CTL_WIDGET_HP_SWITCH,
- "Headphone as Line Out Switch", 0);
+ "Headphone as Line Out Switch",
+ cfg->hp_pins[cfg->hp_outs - 1]);
if (err < 0)
return err;
}
@@ -3786,11 +3788,30 @@ static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
return 0;
}
+/* return non-zero if the hp-pin of the given array index isn't
+ * a jack-detection target
+ */
+static int no_hp_sensing(struct sigmatel_spec *spec, int i)
+{
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ /* ignore sensing of shared line and mic jacks */
+ if (spec->line_switch &&
+ cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE])
+ return 1;
+ if (spec->mic_switch &&
+ cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC])
+ return 1;
+ /* ignore if the pin is set as line-out */
+ if (cfg->hp_pins[i] == spec->hp_switch)
+ return 1;
+ return 0;
+}
+
static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- int nid = cfg->hp_pins[cfg->hp_outs - 1];
int i, presence;
presence = 0;
@@ -3801,15 +3822,16 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
for (i = 0; i < cfg->hp_outs; i++) {
if (presence)
break;
- if (spec->hp_switch && cfg->hp_pins[i] == nid)
- break;
+ if (no_hp_sensing(spec, i))
+ continue;
presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
}
if (presence) {
- /* disable lineouts, enable hp */
+ /* disable lineouts */
if (spec->hp_switch)
- stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+ stac92xx_reset_pinctl(codec, spec->hp_switch,
+ AC_PINCTL_OUT_EN);
for (i = 0; i < cfg->line_outs; i++)
stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
@@ -3821,9 +3843,10 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
spec->gpio_dir, spec->gpio_data &
~spec->eapd_mask);
} else {
- /* enable lineouts, disable hp */
+ /* enable lineouts */
if (spec->hp_switch)
- stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+ stac92xx_set_pinctl(codec, spec->hp_switch,
+ AC_PINCTL_OUT_EN);
for (i = 0; i < cfg->line_outs; i++)
stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
@@ -3835,8 +3858,16 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
spec->gpio_dir, spec->gpio_data |
spec->eapd_mask);
}
- if (!spec->hp_switch && cfg->hp_outs > 1 && presence)
- stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+ /* toggle hp outs */
+ for (i = 0; i < cfg->hp_outs; i++) {
+ unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
+ if (no_hp_sensing(spec, i))
+ continue;
+ if (presence)
+ stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
+ else
+ stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
+ }
}
static void stac92xx_pin_sense(struct hda_codec *codec, int idx)