ALSA: hda - Allow jack state to depend on another jack
authorDylan Reid <dgreid@chromium.org>
Mon, 19 Nov 2012 18:48:07 +0000 (19:48 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 19 Nov 2012 18:49:58 +0000 (19:49 +0100)
Introduce the concept of a "gated" jack.  The gated jack's pin sense
is
only valid when the "gating" jack is plugged.  This requires checking
the gating jack when the gated jack changes and re-checking the gated
jack when the gating jack is plugged/unplugged.

This allows handling of devices where the mic jack detect floats when
the headphone jack is unplugged.

[Rewritten for fixing the possible snd_array reallocation, covering
 the missing callback calls and jack sync operations, as well as some
 code cleanups -- tiwai]

Signed-off-by: Dylan Reid <dgreid@chromium.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h

index 5bdbadaa20fec32cd5b8e7242964f6a536c3af0e..4e19480013381ebd4fdec9f67419fdcf9d0b008c 100644 (file)
@@ -122,6 +122,8 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
        snd_array_free(&codec->jacktbl);
 }
 
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
+
 /* update the cached value and notification flag if needed */
 static void jack_detect_update(struct hda_codec *codec,
                               struct hda_jack_tbl *jack)
@@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec,
        else
                jack->pin_sense = read_pin_sense(codec, jack->nid);
 
+       /* A gating jack indicates the jack is invalid if gating is unplugged */
+       if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
+               jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
+
        jack->jack_dirty = 0;
+
+       /* If a jack is gated by this one update it. */
+       if (jack->gated_jack) {
+               struct hda_jack_tbl *gated =
+                       snd_hda_jack_tbl_get(codec, jack->gated_jack);
+               if (gated) {
+                       gated->jack_dirty = 1;
+                       jack_detect_update(codec, gated);
+               }
+       }
 }
 
 /**
@@ -173,8 +189,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 }
 EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
 
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
-
 /**
  * snd_hda_jack_detect - query pin Presence Detect status
  * @codec: the CODEC to sense
@@ -221,17 +235,47 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
 
+/**
+ * snd_hda_jack_set_gating_jack - Set gating jack.
+ *
+ * Indicates the gated jack is only valid when the gating jack is plugged.
+ */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+                                hda_nid_t gating_nid)
+{
+       struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid);
+       struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid);
+
+       if (!gated || !gating)
+               return -EINVAL;
+
+       gated->gating_jack = gating_nid;
+       gating->gated_jack = gated_nid;
+
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_set_gating_jack);
+
 /**
  * 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;
+       struct hda_jack_tbl *jack;
        int i, state;
 
+       /* update all jacks at first */
+       jack = codec->jacktbl.list;
        for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->nid) {
+               if (jack->nid)
                        jack_detect_update(codec, jack);
+
+       /* report the updated jacks; it's done after updating all jacks
+        * to make sure that all gating jacks properly have been set
+        */
+       jack = codec->jacktbl.list;
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->nid) {
                        if (!jack->kctl)
                                continue;
                        state = get_jack_plug_state(jack->pin_sense);
@@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
 
+static void call_jack_callback(struct hda_codec *codec,
+                              struct hda_jack_tbl *jack)
+{
+       if (jack->callback)
+               jack->callback(codec, jack);
+       if (jack->gated_jack) {
+               struct hda_jack_tbl *gated =
+                       snd_hda_jack_tbl_get(codec, jack->gated_jack);
+               if (gated && gated->callback)
+                       gated->callback(codec, gated);
+       }
+}
+
 void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
 {
        struct hda_jack_tbl *event;
@@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
                return;
        event->jack_dirty = 1;
 
-       if (event->callback)
-               event->callback(codec, event);
-
+       call_jack_callback(codec, event);
        snd_hda_jack_report_sync(codec);
 }
 EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event);
@@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec)
                if (old_sense == get_jack_plug_state(jack->pin_sense))
                        continue;
                changes = 1;
-               if (jack->callback)
-                       jack->callback(codec, jack);
+               call_jack_callback(codec, jack);
        }
        if (changes)
                snd_hda_jack_report_sync(codec);
index 4487785adf345870fff8f71deb8fdb14b64fd523..ec12abd452631d12b9915e0c75a4c45fdbc07c95 100644 (file)
@@ -28,6 +28,8 @@ struct hda_jack_tbl {
        unsigned int jack_detect:1;     /* capable of jack-detection? */
        unsigned int jack_dirty:1;      /* needs to update? */
        unsigned int phantom_jack:1;    /* a fixed, always present port? */
+       hda_nid_t gating_jack;          /* valid when gating jack plugged */
+       hda_nid_t gated_jack;           /* gated is dependent on this jack */
        struct snd_kcontrol *kctl;      /* assigned kctl for jack-detection */
 #ifdef CONFIG_SND_HDA_INPUT_JACK
        int type;
@@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
                                        unsigned char action,
                                        hda_jack_callback cb);
 
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+                                hda_nid_t gating_nid);
 
 u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
 int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);