summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/pci/hda/hda_jack.c168
-rw-r--r--sound/pci/hda/hda_jack.h12
-rw-r--r--sound/pci/hda/patch_cirrus.c27
-rw-r--r--sound/pci/hda/patch_conexant.c2
-rw-r--r--sound/pci/hda/patch_hdmi.c6
-rw-r--r--sound/pci/hda/patch_realtek.c7
-rw-r--r--sound/pci/hda/patch_sigmatel.c7
-rw-r--r--sound/pci/hda/patch_via.c7
8 files changed, 232 insertions, 4 deletions
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 64b78a2..cee6a00 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/control.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_jack.h"
@@ -76,9 +77,13 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
static void jack_detect_update(struct hda_codec *codec,
struct hda_jack_tbl *jack)
{
- if (jack->jack_dirty) {
- jack->pin_sense = read_pin_sense(codec, jack->nid);
+ if (jack->jack_dirty || !jack->jack_cachable) {
+ unsigned int val = read_pin_sense(codec, jack->nid);
jack->jack_dirty = 0;
+ if (val != jack->pin_sense) {
+ jack->need_notify = 1;
+ jack->pin_sense = val;
+ }
}
}
@@ -141,8 +146,167 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
if (!jack)
return -ENOMEM;
+ if (jack->jack_cachable)
+ return 0; /* already registered */
+ jack->jack_cachable = 1;
return snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | tag);
}
EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
+
+/* queue the notification when needed */
+static void jack_detect_report(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ jack_detect_update(codec, jack);
+ if (jack->need_notify) {
+ snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &jack->kctl->id);
+ jack->need_notify = 0;
+ }
+}
+
+/**
+ * snd_hda_jack_report - notify kctl when the jack state was changed
+ */
+void snd_hda_jack_report(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
+
+ if (jack)
+ jack_detect_report(codec, jack);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_report);
+
+/**
+ * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
+ */
+void snd_hda_jack_report_sync(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid) {
+ jack_detect_update(codec, jack);
+ jack_detect_report(codec, jack);
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_report_sync);
+
+/*
+ * jack-detection kcontrols
+ */
+
+#define jack_detect_kctl_info snd_ctl_boolean_mono_info
+
+static int jack_detect_kctl_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;
+
+ ucontrol->value.integer.value[0] = snd_hda_jack_detect(codec, nid);
+ return 0;
+}
+
+static struct snd_kcontrol_new jack_detect_kctl = {
+ /* name is filled later */
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = jack_detect_kctl_info,
+ .get = jack_detect_kctl_get,
+};
+
+/**
+ * snd_hda_jack_add_kctl - Add a kctl for the given pin
+ *
+ * This assigns a jack-detection kctl to the given pin. The kcontrol
+ * will have the given name and index.
+ */
+int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx)
+{
+ struct hda_jack_tbl *jack;
+ struct snd_kcontrol *kctl;
+
+ jack = snd_hda_jack_tbl_get(codec, nid);
+ if (!jack)
+ return 0;
+ if (jack->kctl)
+ return 0; /* already created */
+ kctl = snd_ctl_new1(&jack_detect_kctl, codec);
+ if (!kctl)
+ return -ENOMEM;
+ snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name);
+ kctl->id.index = idx;
+ kctl->private_value = nid;
+ if (snd_hda_ctl_add(codec, nid, kctl) < 0)
+ return -ENOMEM;
+ jack->kctl = kctl;
+ return 0;
+}
+
+static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, int idx,
+ const struct auto_pin_cfg *cfg)
+{
+ if (!nid)
+ return 0;
+ if (!is_jack_detectable(codec, nid))
+ return 0;
+ return snd_hda_jack_add_kctl(codec, nid,
+ snd_hda_get_pin_label(codec, nid, cfg),
+ idx);
+}
+
+/**
+ * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
+ *
+ * As of now, it assigns only to the pins that enabled the detection.
+ * Usually this is called at the end of build_controls callback.
+ */
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ const hda_nid_t *p;
+ int i, err;
+
+ for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, i, cfg);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, i, cfg);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, i, cfg);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ err = add_jack_kctl(codec, cfg->inputs[i].pin, 0, cfg);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, i, cfg);
+ if (err < 0)
+ return err;
+ }
+ err = add_jack_kctl(codec, cfg->dig_in_pin, 0, cfg);
+ if (err < 0)
+ return err;
+ err = add_jack_kctl(codec, cfg->mono_out_pin, 0, cfg);
+ if (err < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index 5c1bcb8..b5983ea 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -15,7 +15,10 @@
struct hda_jack_tbl {
hda_nid_t nid;
unsigned int pin_sense; /* cached pin-sense value */
+ unsigned int jack_cachable:1; /* can be updated via unsol events */
unsigned int jack_dirty:1; /* needs to update? */
+ unsigned int need_notify:1; /* to be notified? */
+ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
};
struct hda_jack_tbl *
@@ -60,4 +63,13 @@ static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
return true;
}
+int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, int idx);
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg);
+
+void snd_hda_jack_report(struct hda_codec *codec, hda_nid_t nid);
+void snd_hda_jack_report_sync(struct hda_codec *codec);
+
+
#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 6f15877..135fd49 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -1192,11 +1192,14 @@ static int cs_init(struct hda_codec *codec)
init_output(codec);
init_input(codec);
init_digital(codec);
+ snd_hda_jack_report_sync(codec);
+
return 0;
}
static int cs_build_controls(struct hda_codec *codec)
{
+ struct cs_spec *spec = codec->spec;
int err;
err = build_output(codec);
@@ -1211,7 +1214,15 @@ static int cs_build_controls(struct hda_codec *codec)
err = build_digital_input(codec);
if (err < 0)
return err;
- return cs_init(codec);
+ err = cs_init(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ return 0;
}
static void cs_free(struct hda_codec *codec)
@@ -1234,6 +1245,7 @@ static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
cs_automic(codec);
break;
}
+ snd_hda_jack_report_sync(codec);
}
static const struct hda_codec_ops cs_patch_ops = {
@@ -1611,6 +1623,7 @@ static int cs421x_init(struct hda_codec *codec)
init_output(codec);
init_input(codec);
init_cs421x_digital(codec);
+ snd_hda_jack_report_sync(codec);
return 0;
}
@@ -1786,6 +1799,7 @@ static int build_cs421x_output(struct hda_codec *codec)
static int cs421x_build_controls(struct hda_codec *codec)
{
+ struct cs_spec *spec = codec->spec;
int err;
err = build_cs421x_output(codec);
@@ -1797,7 +1811,15 @@ static int cs421x_build_controls(struct hda_codec *codec)
err = build_digital_output(codec);
if (err < 0)
return err;
- return cs421x_init(codec);
+ err = cs421x_init(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ return 0;
}
static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -1814,6 +1836,7 @@ static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res)
cs_automic(codec);
break;
}
+ snd_hda_jack_report_sync(codec);
}
static int parse_cs421x_input(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 220e567..25fdd1e 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -3770,6 +3770,7 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
snd_hda_input_jack_report(codec, nid);
break;
}
+ snd_hda_jack_report_sync(codec);
}
/* check whether the pin config is suitable for auto-mic switching;
@@ -4095,6 +4096,7 @@ static int cx_auto_init(struct hda_codec *codec)
cx_auto_init_output(codec);
cx_auto_init_input(codec);
cx_auto_init_digital(codec);
+ snd_hda_jack_report_sync(codec);
return 0;
}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index ea6d85d..f01c5ef 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -769,6 +769,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
snd_hda_jack_set_dirty(codec, pin_nid);
hdmi_present_sense(&spec->pins[pin_idx], true);
+ snd_hda_jack_report_sync(codec);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -1268,6 +1269,10 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
+ err = snd_hda_jack_add_kctl(codec, per_pin->pin_nid,
+ "HDMI", pin_idx);
+ if (err < 0)
+ return err;
}
return 0;
@@ -1290,6 +1295,7 @@ static int generic_hdmi_init(struct hda_codec *codec)
INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
snd_hda_eld_proc_new(codec, eld, pin_idx);
}
+ snd_hda_jack_report_sync(codec);
return 0;
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index da9d227..04beae0 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -677,6 +677,7 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
alc_mic_automute(codec);
break;
}
+ snd_hda_jack_report_sync(codec);
}
/* call init functions of standard auto-mute helpers */
@@ -2054,6 +2055,10 @@ static int alc_build_controls(struct hda_codec *codec)
alc_free_kctls(codec); /* no longer needed */
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -2081,6 +2086,8 @@ static int alc_init(struct hda_codec *codec)
alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
+ snd_hda_jack_report_sync(codec);
+
hda_call_check_power_status(codec, 0x01);
return 0;
}
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 97c6df9..90954b8 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -1212,6 +1212,10 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return err;
}
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -4473,6 +4477,8 @@ static int stac92xx_init(struct hda_codec *codec)
stac_toggle_power_map(codec, nid, 0);
}
+ snd_hda_jack_report_sync(codec);
+
/* sync mute LED */
if (spec->gpio_led)
hda_call_check_power_status(codec, 0x01);
@@ -4868,6 +4874,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
return;
snd_hda_jack_set_dirty(codec, event->nid);
handle_unsol_event(codec, event);
+ snd_hda_jack_report_sync(codec);
}
static int hp_blike_system(u32 subsystem_id);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 3467d0c..8529396 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1500,6 +1500,11 @@ static int via_build_controls(struct hda_codec *codec)
analog_low_current_mode(codec);
via_free_kctls(codec); /* no longer needed */
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -1722,6 +1727,7 @@ static void via_unsol_event(struct hda_codec *codec,
via_hp_automute(codec);
else if (res == VIA_GPIO_EVENT)
via_gpio_control(codec);
+ snd_hda_jack_report_sync(codec);
}
#ifdef CONFIG_PM
@@ -2771,6 +2777,7 @@ static int via_init(struct hda_codec *codec)
via_auto_init_unsol_event(codec);
via_hp_automute(codec);
+ snd_hda_jack_report_sync(codec);
return 0;
}
OpenPOWER on IntegriCloud