[alsa-devel] [PATCH 1/2] ALSA: hda - Allow jack state to depend on another jack.

Takashi Iwai tiwai at suse.de
Mon Nov 19 11:55:06 CET 2012


At Sun, 18 Nov 2012 22:56:39 -0800,
Dylan Reid wrote:
> 
> 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.

The snd_array stuff may reallocate the whole chunks, so you can't keep
the pointers pointing to the array elements.  Instead of that, NIDs
should be kept and resolve on demand.

In addition to that...

> 
> Signed-off-by: Dylan Reid <dgreid at chromium.org>
> ---
>  sound/pci/hda/hda_jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
>  sound/pci/hda/hda_jack.h |  4 ++++
>  2 files changed, 48 insertions(+), 2 deletions(-)
> 
> diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
> index 5bdbada..e851d88 100644
> --- a/sound/pci/hda/hda_jack.c
> +++ b/sound/pci/hda/hda_jack.c
> @@ -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,6 +136,27 @@ 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 &&
> +	    !get_jack_plug_state(jack->gating_jack->pin_sense))
> +		jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
> +
> +	/* If a jack is gated by this one update it. */
> +	if (jack->gated_jack) {
> +		struct hda_jack_tbl *gated = jack->gated_jack;
> +
> +		if (gated->callback)
> +			gated->callback(codec, gated);

I don't think this is the right place to call the callback of the
gated pin.  It should be called where the callback of gating pin is
called.

> +		/* If this jack is plugged, then check the gated jack, otherwise
> +		 * not plugged.
> +		 */
> +		if (get_jack_plug_state(jack->pin_sense))
> +			gated->pin_sense = read_pin_sense(codec, gated->nid);
> +		else
> +			gated->pin_sense &= ~AC_PINSENSE_PRESENCE;
> +	}

... and the handling of gated pin can be done more easily by calling
jack_detect_update() again.

And, another place to fix is the jack sync code.
Since now you have a dependency between jacks, the update and check have
to be split to two: update all jacks, and call jack sync for each
pin.  Otherwise, it won't be handled properly if a gating jack appears
in the table after a gated jack.

The below is my revised patch.  Could you check it?


thanks,

Takashi

---
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 5bdbada..4e19480 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -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
@@ -222,16 +236,46 @@ 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);
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index 4487785..ec12abd 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -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);


More information about the Alsa-devel mailing list