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

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
Loading
Loading
Loading
Loading
+63 −9
Original line number Diff line number Diff line
@@ -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);
+4 −0
Original line number Diff line number Diff line
@@ -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);