On Mon, Nov 19, 2012 at 2:55 AM, Takashi Iwai tiwai@suse.de wrote:
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@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?
Tested, works great. Thanks for looking and fixing Takashi.
-Dylan
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);