Commit 0619ba8c authored by Dylan Reid's avatar Dylan Reid Committed by Takashi Iwai

ALSA: hda - Allow jack state to depend on another jack

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: default avatarDylan Reid <dgreid@chromium.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 379170a4
...@@ -122,6 +122,8 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) ...@@ -122,6 +122,8 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
snd_array_free(&codec->jacktbl); snd_array_free(&codec->jacktbl);
} }
#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
/* update the cached value and notification flag if needed */ /* update the cached value and notification flag if needed */
static void jack_detect_update(struct hda_codec *codec, static void jack_detect_update(struct hda_codec *codec,
struct hda_jack_tbl *jack) struct hda_jack_tbl *jack)
...@@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec, ...@@ -134,7 +136,21 @@ static void jack_detect_update(struct hda_codec *codec,
else else
jack->pin_sense = read_pin_sense(codec, jack->nid); 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; 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) ...@@ -173,8 +189,6 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
} }
EXPORT_SYMBOL_HDA(snd_hda_pin_sense); 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 * snd_hda_jack_detect - query pin Presence Detect status
* @codec: the CODEC to sense * @codec: the CODEC to sense
...@@ -221,17 +235,47 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, ...@@ -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); 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 * 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) 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; int i, state;
/* update all jacks at first */
jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++) for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) { if (jack->nid)
jack_detect_update(codec, jack); 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) if (!jack->kctl)
continue; continue;
state = get_jack_plug_state(jack->pin_sense); state = get_jack_plug_state(jack->pin_sense);
...@@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, ...@@ -424,6 +468,19 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
} }
EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls); 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) void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
{ {
struct hda_jack_tbl *event; struct hda_jack_tbl *event;
...@@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) ...@@ -434,9 +491,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
return; return;
event->jack_dirty = 1; event->jack_dirty = 1;
if (event->callback) call_jack_callback(codec, event);
event->callback(codec, event);
snd_hda_jack_report_sync(codec); snd_hda_jack_report_sync(codec);
} }
EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event); EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event);
...@@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) ...@@ -455,8 +510,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec)
if (old_sense == get_jack_plug_state(jack->pin_sense)) if (old_sense == get_jack_plug_state(jack->pin_sense))
continue; continue;
changes = 1; changes = 1;
if (jack->callback) call_jack_callback(codec, jack);
jack->callback(codec, jack);
} }
if (changes) if (changes)
snd_hda_jack_report_sync(codec); snd_hda_jack_report_sync(codec);
......
...@@ -28,6 +28,8 @@ struct hda_jack_tbl { ...@@ -28,6 +28,8 @@ struct hda_jack_tbl {
unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_detect:1; /* capable of jack-detection? */
unsigned int jack_dirty:1; /* needs to update? */ unsigned int jack_dirty:1; /* needs to update? */
unsigned int phantom_jack:1; /* a fixed, always present port? */ 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 */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
#ifdef CONFIG_SND_HDA_INPUT_JACK #ifdef CONFIG_SND_HDA_INPUT_JACK
int type; int type;
...@@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, ...@@ -69,6 +71,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
unsigned char action, unsigned char action,
hda_jack_callback cb); 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); 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); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment