summaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_jack.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_jack.c')
-rw-r--r--sound/pci/hda/hda_jack.c168
1 files changed, 166 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 64b78a2e20e..cee6a00bd85 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);