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: Dylan Reid <dgreid@chromium.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Родитель
379170a42c
Коммит
0619ba8c17
|
@ -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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
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,
|
||||||
}
|
}
|
||||||
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++)
|
||||||
|
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++)
|
for (i = 0; i < codec->jacktbl.used; i++, jack++)
|
||||||
if (jack->nid) {
|
if (jack->nid) {
|
||||||
jack_detect_update(codec, jack);
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
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)
|
||||||
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)
|
||||||
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 {
|
||||||
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,
|
||||||
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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче