162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Jack-detection handling for HD-audio
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <sound/core.h>
1262306a36Sopenharmony_ci#include <sound/control.h>
1362306a36Sopenharmony_ci#include <sound/jack.h>
1462306a36Sopenharmony_ci#include <sound/hda_codec.h>
1562306a36Sopenharmony_ci#include "hda_local.h"
1662306a36Sopenharmony_ci#include "hda_auto_parser.h"
1762306a36Sopenharmony_ci#include "hda_jack.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/**
2062306a36Sopenharmony_ci * is_jack_detectable - Check whether the given pin is jack-detectable
2162306a36Sopenharmony_ci * @codec: the HDA codec
2262306a36Sopenharmony_ci * @nid: pin NID
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Check whether the given pin is capable to report the jack detection.
2562306a36Sopenharmony_ci * The jack detection might not work by various reasons, e.g. the jack
2662306a36Sopenharmony_ci * detection is prohibited in the codec level, the pin config has
2762306a36Sopenharmony_ci * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cibool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	if (codec->no_jack_detect)
3262306a36Sopenharmony_ci		return false;
3362306a36Sopenharmony_ci	if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
3462306a36Sopenharmony_ci		return false;
3562306a36Sopenharmony_ci	if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
3662306a36Sopenharmony_ci	     AC_DEFCFG_MISC_NO_PRESENCE)
3762306a36Sopenharmony_ci		return false;
3862306a36Sopenharmony_ci	if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
3962306a36Sopenharmony_ci	    !codec->jackpoll_interval)
4062306a36Sopenharmony_ci		return false;
4162306a36Sopenharmony_ci	return true;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(is_jack_detectable);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* execute pin sense measurement */
4662306a36Sopenharmony_cistatic u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	u32 pincap;
4962306a36Sopenharmony_ci	u32 val;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!codec->no_trigger_sense) {
5262306a36Sopenharmony_ci		pincap = snd_hda_query_pin_caps(codec, nid);
5362306a36Sopenharmony_ci		if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
5462306a36Sopenharmony_ci			snd_hda_codec_read(codec, nid, 0,
5562306a36Sopenharmony_ci					AC_VERB_SET_PIN_SENSE, 0);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	val = snd_hda_codec_read(codec, nid, 0,
5862306a36Sopenharmony_ci				  AC_VERB_GET_PIN_SENSE, dev_id);
5962306a36Sopenharmony_ci	if (codec->inv_jack_detect)
6062306a36Sopenharmony_ci		val ^= AC_PINSENSE_PRESENCE;
6162306a36Sopenharmony_ci	return val;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/**
6562306a36Sopenharmony_ci * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID
6662306a36Sopenharmony_ci * @codec: the HDA codec
6762306a36Sopenharmony_ci * @nid: pin NID to refer to
6862306a36Sopenharmony_ci * @dev_id: pin device entry id
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistruct hda_jack_tbl *
7162306a36Sopenharmony_cisnd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
7462306a36Sopenharmony_ci	int i;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!nid || !jack)
7762306a36Sopenharmony_ci		return NULL;
7862306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++)
7962306a36Sopenharmony_ci		if (jack->nid == nid && jack->dev_id == dev_id)
8062306a36Sopenharmony_ci			return jack;
8162306a36Sopenharmony_ci	return NULL;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/**
8662306a36Sopenharmony_ci * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
8762306a36Sopenharmony_ci * @codec: the HDA codec
8862306a36Sopenharmony_ci * @tag: tag value to refer to
8962306a36Sopenharmony_ci * @dev_id: pin device entry id
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistruct hda_jack_tbl *
9262306a36Sopenharmony_cisnd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
9362306a36Sopenharmony_ci			      unsigned char tag, int dev_id)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
9662306a36Sopenharmony_ci	int i;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (!tag || !jack)
9962306a36Sopenharmony_ci		return NULL;
10062306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++)
10162306a36Sopenharmony_ci		if (jack->tag == tag && jack->dev_id == dev_id)
10262306a36Sopenharmony_ci			return jack;
10362306a36Sopenharmony_ci	return NULL;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic struct hda_jack_tbl *
10862306a36Sopenharmony_ciany_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
11162306a36Sopenharmony_ci	int i;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (!nid || !jack)
11462306a36Sopenharmony_ci		return NULL;
11562306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++)
11662306a36Sopenharmony_ci		if (jack->nid == nid)
11762306a36Sopenharmony_ci			return jack;
11862306a36Sopenharmony_ci	return NULL;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/**
12262306a36Sopenharmony_ci * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
12362306a36Sopenharmony_ci * @codec: the HDA codec
12462306a36Sopenharmony_ci * @nid: pin NID to assign
12562306a36Sopenharmony_ci * @dev_id: pin device entry id
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_cistatic struct hda_jack_tbl *
12862306a36Sopenharmony_cisnd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct hda_jack_tbl *jack =
13162306a36Sopenharmony_ci		snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
13262306a36Sopenharmony_ci	struct hda_jack_tbl *existing_nid_jack =
13362306a36Sopenharmony_ci		any_jack_tbl_get_from_nid(codec, nid);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	WARN_ON(dev_id != 0 && !codec->dp_mst);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (jack)
13862306a36Sopenharmony_ci		return jack;
13962306a36Sopenharmony_ci	jack = snd_array_new(&codec->jacktbl);
14062306a36Sopenharmony_ci	if (!jack)
14162306a36Sopenharmony_ci		return NULL;
14262306a36Sopenharmony_ci	jack->nid = nid;
14362306a36Sopenharmony_ci	jack->dev_id = dev_id;
14462306a36Sopenharmony_ci	jack->jack_dirty = 1;
14562306a36Sopenharmony_ci	if (existing_nid_jack) {
14662306a36Sopenharmony_ci		jack->tag = existing_nid_jack->tag;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		/*
14962306a36Sopenharmony_ci		 * Copy jack_detect from existing_nid_jack to avoid
15062306a36Sopenharmony_ci		 * snd_hda_jack_detect_enable_callback_mst() making multiple
15162306a36Sopenharmony_ci		 * SET_UNSOLICITED_ENABLE calls on the same pin.
15262306a36Sopenharmony_ci		 */
15362306a36Sopenharmony_ci		jack->jack_detect = existing_nid_jack->jack_detect;
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		jack->tag = codec->jacktbl.used;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return jack;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_civoid snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
16462306a36Sopenharmony_ci	int i;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++) {
16762306a36Sopenharmony_ci		if (!codec->bus->shutdown && jack->jack)
16862306a36Sopenharmony_ci			snd_device_disconnect(codec->card, jack->jack);
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_civoid snd_hda_jack_tbl_clear(struct hda_codec *codec)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
17562306a36Sopenharmony_ci	int i;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++) {
17862306a36Sopenharmony_ci		struct hda_jack_callback *cb, *next;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		/* free jack instances manually when clearing/reconfiguring */
18162306a36Sopenharmony_ci		if (!codec->bus->shutdown && jack->jack)
18262306a36Sopenharmony_ci			snd_device_free(codec->card, jack->jack);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		for (cb = jack->callback; cb; cb = next) {
18562306a36Sopenharmony_ci			next = cb->next;
18662306a36Sopenharmony_ci			kfree(cb);
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	snd_array_free(&codec->jacktbl);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* update the cached value and notification flag if needed */
19562306a36Sopenharmony_cistatic void jack_detect_update(struct hda_codec *codec,
19662306a36Sopenharmony_ci			       struct hda_jack_tbl *jack)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	if (!jack->jack_dirty)
19962306a36Sopenharmony_ci		return;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (jack->phantom_jack)
20262306a36Sopenharmony_ci		jack->pin_sense = AC_PINSENSE_PRESENCE;
20362306a36Sopenharmony_ci	else
20462306a36Sopenharmony_ci		jack->pin_sense = read_pin_sense(codec, jack->nid,
20562306a36Sopenharmony_ci						 jack->dev_id);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* A gating jack indicates the jack is invalid if gating is unplugged */
20862306a36Sopenharmony_ci	if (jack->gating_jack &&
20962306a36Sopenharmony_ci	    !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id))
21062306a36Sopenharmony_ci		jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	jack->jack_dirty = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* If a jack is gated by this one update it. */
21562306a36Sopenharmony_ci	if (jack->gated_jack) {
21662306a36Sopenharmony_ci		struct hda_jack_tbl *gated =
21762306a36Sopenharmony_ci			snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
21862306a36Sopenharmony_ci						 jack->dev_id);
21962306a36Sopenharmony_ci		if (gated) {
22062306a36Sopenharmony_ci			gated->jack_dirty = 1;
22162306a36Sopenharmony_ci			jack_detect_update(codec, gated);
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/**
22762306a36Sopenharmony_ci * snd_hda_jack_set_dirty_all - Mark all the cached as dirty
22862306a36Sopenharmony_ci * @codec: the HDA codec
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * This function sets the dirty flag to all entries of jack table.
23162306a36Sopenharmony_ci * It's called from the resume path in hda_codec.c.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_civoid snd_hda_jack_set_dirty_all(struct hda_codec *codec)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
23662306a36Sopenharmony_ci	int i;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++)
23962306a36Sopenharmony_ci		if (jack->nid)
24062306a36Sopenharmony_ci			jack->jack_dirty = 1;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci/**
24562306a36Sopenharmony_ci * snd_hda_jack_pin_sense - execute pin sense measurement
24662306a36Sopenharmony_ci * @codec: the CODEC to sense
24762306a36Sopenharmony_ci * @nid: the pin NID to sense
24862306a36Sopenharmony_ci * @dev_id: pin device entry id
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Execute necessary pin sense measurement and return its Presence Detect,
25162306a36Sopenharmony_ci * Impedance, ELD Valid etc. status bits.
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_ciu32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct hda_jack_tbl *jack =
25662306a36Sopenharmony_ci		snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
25762306a36Sopenharmony_ci	if (jack) {
25862306a36Sopenharmony_ci		jack_detect_update(codec, jack);
25962306a36Sopenharmony_ci		return jack->pin_sense;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci	return read_pin_sense(codec, nid, dev_id);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/**
26662306a36Sopenharmony_ci * snd_hda_jack_detect_state_mst - query pin Presence Detect status
26762306a36Sopenharmony_ci * @codec: the CODEC to sense
26862306a36Sopenharmony_ci * @nid: the pin NID to sense
26962306a36Sopenharmony_ci * @dev_id: pin device entry id
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * Query and return the pin's Presence Detect status, as either
27262306a36Sopenharmony_ci * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_ciint snd_hda_jack_detect_state_mst(struct hda_codec *codec,
27562306a36Sopenharmony_ci				  hda_nid_t nid, int dev_id)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct hda_jack_tbl *jack =
27862306a36Sopenharmony_ci		snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
27962306a36Sopenharmony_ci	if (jack && jack->phantom_jack)
28062306a36Sopenharmony_ci		return HDA_JACK_PHANTOM;
28162306a36Sopenharmony_ci	else if (snd_hda_jack_pin_sense(codec, nid, dev_id) &
28262306a36Sopenharmony_ci		 AC_PINSENSE_PRESENCE)
28362306a36Sopenharmony_ci		return HDA_JACK_PRESENT;
28462306a36Sopenharmony_ci	else
28562306a36Sopenharmony_ci		return HDA_JACK_NOT_PRESENT;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic struct hda_jack_callback *
29062306a36Sopenharmony_cifind_callback_from_list(struct hda_jack_tbl *jack,
29162306a36Sopenharmony_ci			hda_jack_callback_fn func)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct hda_jack_callback *cb;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!func)
29662306a36Sopenharmony_ci		return NULL;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	for (cb = jack->callback; cb; cb = cb->next) {
29962306a36Sopenharmony_ci		if (cb->func == func)
30062306a36Sopenharmony_ci			return cb;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return NULL;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/**
30762306a36Sopenharmony_ci * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection
30862306a36Sopenharmony_ci * @codec: the HDA codec
30962306a36Sopenharmony_ci * @nid: pin NID to enable
31062306a36Sopenharmony_ci * @func: callback function to register
31162306a36Sopenharmony_ci * @dev_id: pin device entry id
31262306a36Sopenharmony_ci *
31362306a36Sopenharmony_ci * In the case of error, the return value will be a pointer embedded with
31462306a36Sopenharmony_ci * errno.  Check and handle the return value appropriately with standard
31562306a36Sopenharmony_ci * macros such as @IS_ERR() and @PTR_ERR().
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_cistruct hda_jack_callback *
31862306a36Sopenharmony_cisnd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
31962306a36Sopenharmony_ci					int dev_id, hda_jack_callback_fn func)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct hda_jack_tbl *jack;
32262306a36Sopenharmony_ci	struct hda_jack_callback *callback = NULL;
32362306a36Sopenharmony_ci	int err;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
32662306a36Sopenharmony_ci	if (!jack)
32762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	callback = find_callback_from_list(jack, func);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (func && !callback) {
33262306a36Sopenharmony_ci		callback = kzalloc(sizeof(*callback), GFP_KERNEL);
33362306a36Sopenharmony_ci		if (!callback)
33462306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
33562306a36Sopenharmony_ci		callback->func = func;
33662306a36Sopenharmony_ci		callback->nid = jack->nid;
33762306a36Sopenharmony_ci		callback->dev_id = jack->dev_id;
33862306a36Sopenharmony_ci		callback->next = jack->callback;
33962306a36Sopenharmony_ci		jack->callback = callback;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (jack->jack_detect)
34362306a36Sopenharmony_ci		return callback; /* already registered */
34462306a36Sopenharmony_ci	jack->jack_detect = 1;
34562306a36Sopenharmony_ci	if (codec->jackpoll_interval > 0)
34662306a36Sopenharmony_ci		return callback; /* No unsol if we're polling instead */
34762306a36Sopenharmony_ci	err = snd_hda_codec_write_cache(codec, nid, 0,
34862306a36Sopenharmony_ci					 AC_VERB_SET_UNSOLICITED_ENABLE,
34962306a36Sopenharmony_ci					 AC_USRSP_EN | jack->tag);
35062306a36Sopenharmony_ci	if (err < 0)
35162306a36Sopenharmony_ci		return ERR_PTR(err);
35262306a36Sopenharmony_ci	return callback;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/**
35762306a36Sopenharmony_ci * snd_hda_jack_detect_enable - Enable the jack detection on the given pin
35862306a36Sopenharmony_ci * @codec: the HDA codec
35962306a36Sopenharmony_ci * @nid: pin NID to enable jack detection
36062306a36Sopenharmony_ci * @dev_id: pin device entry id
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci * Enable the jack detection with the default callback.  Returns zero if
36362306a36Sopenharmony_ci * successful or a negative error code.
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_ciint snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
36662306a36Sopenharmony_ci			       int dev_id)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec,
36962306a36Sopenharmony_ci								       nid,
37062306a36Sopenharmony_ci								       dev_id,
37162306a36Sopenharmony_ci								       NULL));
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/**
37662306a36Sopenharmony_ci * snd_hda_jack_set_gating_jack - Set gating jack.
37762306a36Sopenharmony_ci * @codec: the HDA codec
37862306a36Sopenharmony_ci * @gated_nid: gated pin NID
37962306a36Sopenharmony_ci * @gating_nid: gating pin NID
38062306a36Sopenharmony_ci *
38162306a36Sopenharmony_ci * Indicates the gated jack is only valid when the gating jack is plugged.
38262306a36Sopenharmony_ci */
38362306a36Sopenharmony_ciint snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
38462306a36Sopenharmony_ci				 hda_nid_t gating_nid)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0);
38762306a36Sopenharmony_ci	struct hda_jack_tbl *gating =
38862306a36Sopenharmony_ci		snd_hda_jack_tbl_new(codec, gating_nid, 0);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	WARN_ON(codec->dp_mst);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (!gated || !gating)
39362306a36Sopenharmony_ci		return -EINVAL;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	gated->gating_jack = gating_nid;
39662306a36Sopenharmony_ci	gating->gated_jack = gated_nid;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/**
40362306a36Sopenharmony_ci * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
40462306a36Sopenharmony_ci * @codec: the HDA codec
40562306a36Sopenharmony_ci * @key_nid: key event is generated by this pin NID
40662306a36Sopenharmony_ci * @keymap: map of key type and key code
40762306a36Sopenharmony_ci * @jack_nid: key reports to the jack of this pin NID
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * This function is used in the case of key is generated from one NID while is
41062306a36Sopenharmony_ci * reported to the jack of another NID.
41162306a36Sopenharmony_ci */
41262306a36Sopenharmony_ciint snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
41362306a36Sopenharmony_ci			     const struct hda_jack_keymap *keymap,
41462306a36Sopenharmony_ci			     hda_nid_t jack_nid)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	const struct hda_jack_keymap *map;
41762306a36Sopenharmony_ci	struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid);
41862306a36Sopenharmony_ci	struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	WARN_ON(codec->dp_mst);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (!key_gen || !report_to || !report_to->jack)
42362306a36Sopenharmony_ci		return -EINVAL;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	key_gen->key_report_jack = jack_nid;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (keymap)
42862306a36Sopenharmony_ci		for (map = keymap; map->type; map++)
42962306a36Sopenharmony_ci			snd_jack_set_key(report_to->jack, map->type, map->key);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci/**
43662306a36Sopenharmony_ci * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state.
43762306a36Sopenharmony_ci * @codec: the HDA codec
43862306a36Sopenharmony_ci * @jack_nid: the button event reports to the jack_tbl of this NID
43962306a36Sopenharmony_ci * @button_state: the button event captured by codec
44062306a36Sopenharmony_ci *
44162306a36Sopenharmony_ci * Codec driver calls this function to report the button event.
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_civoid snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
44462306a36Sopenharmony_ci				   int button_state)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (!jack)
44962306a36Sopenharmony_ci		return;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (jack->key_report_jack) {
45262306a36Sopenharmony_ci		struct hda_jack_tbl *report_to =
45362306a36Sopenharmony_ci			snd_hda_jack_tbl_get(codec, jack->key_report_jack);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		if (report_to) {
45662306a36Sopenharmony_ci			report_to->button_state = button_state;
45762306a36Sopenharmony_ci			return;
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	jack->button_state = button_state;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci/**
46662306a36Sopenharmony_ci * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
46762306a36Sopenharmony_ci * @codec: the HDA codec
46862306a36Sopenharmony_ci */
46962306a36Sopenharmony_civoid snd_hda_jack_report_sync(struct hda_codec *codec)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct hda_jack_tbl *jack;
47262306a36Sopenharmony_ci	int i, state;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* update all jacks at first */
47562306a36Sopenharmony_ci	jack = codec->jacktbl.list;
47662306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++)
47762306a36Sopenharmony_ci		if (jack->nid)
47862306a36Sopenharmony_ci			jack_detect_update(codec, jack);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* report the updated jacks; it's done after updating all jacks
48162306a36Sopenharmony_ci	 * to make sure that all gating jacks properly have been set
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	jack = codec->jacktbl.list;
48462306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++)
48562306a36Sopenharmony_ci		if (jack->nid) {
48662306a36Sopenharmony_ci			if (!jack->jack || jack->block_report)
48762306a36Sopenharmony_ci				continue;
48862306a36Sopenharmony_ci			state = jack->button_state;
48962306a36Sopenharmony_ci			if (get_jack_plug_state(jack->pin_sense))
49062306a36Sopenharmony_ci				state |= jack->type;
49162306a36Sopenharmony_ci			snd_jack_report(jack->jack, state);
49262306a36Sopenharmony_ci			if (jack->button_state) {
49362306a36Sopenharmony_ci				snd_jack_report(jack->jack,
49462306a36Sopenharmony_ci						state & ~jack->button_state);
49562306a36Sopenharmony_ci				jack->button_state = 0; /* button released */
49662306a36Sopenharmony_ci			}
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/* guess the jack type from the pin-config */
50262306a36Sopenharmony_cistatic int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
50562306a36Sopenharmony_ci	switch (get_defcfg_device(def_conf)) {
50662306a36Sopenharmony_ci	case AC_JACK_LINE_OUT:
50762306a36Sopenharmony_ci	case AC_JACK_SPEAKER:
50862306a36Sopenharmony_ci		return SND_JACK_LINEOUT;
50962306a36Sopenharmony_ci	case AC_JACK_HP_OUT:
51062306a36Sopenharmony_ci		return SND_JACK_HEADPHONE;
51162306a36Sopenharmony_ci	case AC_JACK_SPDIF_OUT:
51262306a36Sopenharmony_ci	case AC_JACK_DIG_OTHER_OUT:
51362306a36Sopenharmony_ci		return SND_JACK_AVOUT;
51462306a36Sopenharmony_ci	case AC_JACK_MIC_IN:
51562306a36Sopenharmony_ci		return SND_JACK_MICROPHONE;
51662306a36Sopenharmony_ci	default:
51762306a36Sopenharmony_ci		return SND_JACK_LINEIN;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void hda_free_jack_priv(struct snd_jack *jack)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct hda_jack_tbl *jacks = jack->private_data;
52462306a36Sopenharmony_ci	jacks->nid = 0;
52562306a36Sopenharmony_ci	jacks->jack = NULL;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/**
52962306a36Sopenharmony_ci * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin
53062306a36Sopenharmony_ci * @codec: the HDA codec
53162306a36Sopenharmony_ci * @nid: pin NID to assign
53262306a36Sopenharmony_ci * @dev_id : pin device entry id
53362306a36Sopenharmony_ci * @name: string name for the jack
53462306a36Sopenharmony_ci * @phantom_jack: flag to deal as a phantom jack
53562306a36Sopenharmony_ci * @type: jack type bits to be reported, 0 for guessing from pincfg
53662306a36Sopenharmony_ci * @keymap: optional jack / key mapping
53762306a36Sopenharmony_ci *
53862306a36Sopenharmony_ci * This assigns a jack-detection kctl to the given pin.  The kcontrol
53962306a36Sopenharmony_ci * will have the given name and index.
54062306a36Sopenharmony_ci */
54162306a36Sopenharmony_ciint snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
54262306a36Sopenharmony_ci			      int dev_id, const char *name, bool phantom_jack,
54362306a36Sopenharmony_ci			      int type, const struct hda_jack_keymap *keymap)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct hda_jack_tbl *jack;
54662306a36Sopenharmony_ci	const struct hda_jack_keymap *map;
54762306a36Sopenharmony_ci	int err, state, buttons;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
55062306a36Sopenharmony_ci	if (!jack)
55162306a36Sopenharmony_ci		return 0;
55262306a36Sopenharmony_ci	if (jack->jack)
55362306a36Sopenharmony_ci		return 0; /* already created */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (!type)
55662306a36Sopenharmony_ci		type = get_input_jack_type(codec, nid);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	buttons = 0;
55962306a36Sopenharmony_ci	if (keymap) {
56062306a36Sopenharmony_ci		for (map = keymap; map->type; map++)
56162306a36Sopenharmony_ci			buttons |= map->type;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	err = snd_jack_new(codec->card, name, type | buttons,
56562306a36Sopenharmony_ci			   &jack->jack, true, phantom_jack);
56662306a36Sopenharmony_ci	if (err < 0)
56762306a36Sopenharmony_ci		return err;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	jack->phantom_jack = !!phantom_jack;
57062306a36Sopenharmony_ci	jack->type = type;
57162306a36Sopenharmony_ci	jack->button_state = 0;
57262306a36Sopenharmony_ci	jack->jack->private_data = jack;
57362306a36Sopenharmony_ci	jack->jack->private_free = hda_free_jack_priv;
57462306a36Sopenharmony_ci	if (keymap) {
57562306a36Sopenharmony_ci		for (map = keymap; map->type; map++)
57662306a36Sopenharmony_ci			snd_jack_set_key(jack->jack, map->type, map->key);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	state = snd_hda_jack_detect_mst(codec, nid, dev_id);
58062306a36Sopenharmony_ci	snd_jack_report(jack->jack, state ? jack->type : 0);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return 0;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
58762306a36Sopenharmony_ci			 const struct auto_pin_cfg *cfg,
58862306a36Sopenharmony_ci			 const char *base_name)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	unsigned int def_conf, conn;
59162306a36Sopenharmony_ci	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
59262306a36Sopenharmony_ci	int err;
59362306a36Sopenharmony_ci	bool phantom_jack;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	WARN_ON(codec->dp_mst);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (!nid)
59862306a36Sopenharmony_ci		return 0;
59962306a36Sopenharmony_ci	def_conf = snd_hda_codec_get_pincfg(codec, nid);
60062306a36Sopenharmony_ci	conn = get_defcfg_connect(def_conf);
60162306a36Sopenharmony_ci	if (conn == AC_JACK_PORT_NONE)
60262306a36Sopenharmony_ci		return 0;
60362306a36Sopenharmony_ci	phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
60462306a36Sopenharmony_ci		       !is_jack_detectable(codec, nid);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (base_name)
60762306a36Sopenharmony_ci		strscpy(name, base_name, sizeof(name));
60862306a36Sopenharmony_ci	else
60962306a36Sopenharmony_ci		snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL);
61062306a36Sopenharmony_ci	if (phantom_jack)
61162306a36Sopenharmony_ci		/* Example final name: "Internal Mic Phantom Jack" */
61262306a36Sopenharmony_ci		strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
61362306a36Sopenharmony_ci	err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL);
61462306a36Sopenharmony_ci	if (err < 0)
61562306a36Sopenharmony_ci		return err;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (!phantom_jack)
61862306a36Sopenharmony_ci		return snd_hda_jack_detect_enable(codec, nid, 0);
61962306a36Sopenharmony_ci	return 0;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci/**
62362306a36Sopenharmony_ci * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
62462306a36Sopenharmony_ci * @codec: the HDA codec
62562306a36Sopenharmony_ci * @cfg: pin config table to parse
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_ciint snd_hda_jack_add_kctls(struct hda_codec *codec,
62862306a36Sopenharmony_ci			   const struct auto_pin_cfg *cfg)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	const hda_nid_t *p;
63162306a36Sopenharmony_ci	int i, err;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	for (i = 0; i < cfg->num_inputs; i++) {
63462306a36Sopenharmony_ci		/* If we have headphone mics; make sure they get the right name
63562306a36Sopenharmony_ci		   before grabbed by output pins */
63662306a36Sopenharmony_ci		if (cfg->inputs[i].is_headphone_mic) {
63762306a36Sopenharmony_ci			if (auto_cfg_hp_outs(cfg) == 1)
63862306a36Sopenharmony_ci				err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
63962306a36Sopenharmony_ci						    cfg, "Headphone Mic");
64062306a36Sopenharmony_ci			else
64162306a36Sopenharmony_ci				err = add_jack_kctl(codec, cfg->inputs[i].pin,
64262306a36Sopenharmony_ci						    cfg, "Headphone Mic");
64362306a36Sopenharmony_ci		} else
64462306a36Sopenharmony_ci			err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
64562306a36Sopenharmony_ci					    NULL);
64662306a36Sopenharmony_ci		if (err < 0)
64762306a36Sopenharmony_ci			return err;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
65162306a36Sopenharmony_ci		err = add_jack_kctl(codec, *p, cfg, NULL);
65262306a36Sopenharmony_ci		if (err < 0)
65362306a36Sopenharmony_ci			return err;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
65662306a36Sopenharmony_ci		if (*p == *cfg->line_out_pins) /* might be duplicated */
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci		err = add_jack_kctl(codec, *p, cfg, NULL);
65962306a36Sopenharmony_ci		if (err < 0)
66062306a36Sopenharmony_ci			return err;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci	for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
66362306a36Sopenharmony_ci		if (*p == *cfg->line_out_pins) /* might be duplicated */
66462306a36Sopenharmony_ci			break;
66562306a36Sopenharmony_ci		err = add_jack_kctl(codec, *p, cfg, NULL);
66662306a36Sopenharmony_ci		if (err < 0)
66762306a36Sopenharmony_ci			return err;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci	for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
67062306a36Sopenharmony_ci		err = add_jack_kctl(codec, *p, cfg, NULL);
67162306a36Sopenharmony_ci		if (err < 0)
67262306a36Sopenharmony_ci			return err;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci	err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
67562306a36Sopenharmony_ci	if (err < 0)
67662306a36Sopenharmony_ci		return err;
67762306a36Sopenharmony_ci	err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
67862306a36Sopenharmony_ci	if (err < 0)
67962306a36Sopenharmony_ci		return err;
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void call_jack_callback(struct hda_codec *codec, unsigned int res,
68562306a36Sopenharmony_ci			       struct hda_jack_tbl *jack)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct hda_jack_callback *cb;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	for (cb = jack->callback; cb; cb = cb->next) {
69062306a36Sopenharmony_ci		cb->jack = jack;
69162306a36Sopenharmony_ci		cb->unsol_res = res;
69262306a36Sopenharmony_ci		cb->func(codec, cb);
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci	if (jack->gated_jack) {
69562306a36Sopenharmony_ci		struct hda_jack_tbl *gated =
69662306a36Sopenharmony_ci			snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
69762306a36Sopenharmony_ci						 jack->dev_id);
69862306a36Sopenharmony_ci		if (gated) {
69962306a36Sopenharmony_ci			for (cb = gated->callback; cb; cb = cb->next) {
70062306a36Sopenharmony_ci				cb->jack = gated;
70162306a36Sopenharmony_ci				cb->unsol_res = res;
70262306a36Sopenharmony_ci				cb->func(codec, cb);
70362306a36Sopenharmony_ci			}
70462306a36Sopenharmony_ci		}
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci/**
70962306a36Sopenharmony_ci * snd_hda_jack_unsol_event - Handle an unsolicited event
71062306a36Sopenharmony_ci * @codec: the HDA codec
71162306a36Sopenharmony_ci * @res: the unsolicited event data
71262306a36Sopenharmony_ci */
71362306a36Sopenharmony_civoid snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct hda_jack_tbl *event;
71662306a36Sopenharmony_ci	int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (codec->dp_mst) {
71962306a36Sopenharmony_ci		int dev_entry =
72062306a36Sopenharmony_ci			(res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
72362306a36Sopenharmony_ci	} else {
72462306a36Sopenharmony_ci		event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci	if (!event)
72762306a36Sopenharmony_ci		return;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (event->key_report_jack) {
73062306a36Sopenharmony_ci		struct hda_jack_tbl *report_to =
73162306a36Sopenharmony_ci			snd_hda_jack_tbl_get_mst(codec, event->key_report_jack,
73262306a36Sopenharmony_ci						 event->dev_id);
73362306a36Sopenharmony_ci		if (report_to)
73462306a36Sopenharmony_ci			report_to->jack_dirty = 1;
73562306a36Sopenharmony_ci	} else
73662306a36Sopenharmony_ci		event->jack_dirty = 1;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	call_jack_callback(codec, res, event);
73962306a36Sopenharmony_ci	snd_hda_jack_report_sync(codec);
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/**
74462306a36Sopenharmony_ci * snd_hda_jack_poll_all - Poll all jacks
74562306a36Sopenharmony_ci * @codec: the HDA codec
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * Poll all detectable jacks with dirty flag, update the status, call
74862306a36Sopenharmony_ci * callbacks and call snd_hda_jack_report_sync() if any changes are found.
74962306a36Sopenharmony_ci */
75062306a36Sopenharmony_civoid snd_hda_jack_poll_all(struct hda_codec *codec)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct hda_jack_tbl *jack = codec->jacktbl.list;
75362306a36Sopenharmony_ci	int i, changes = 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	for (i = 0; i < codec->jacktbl.used; i++, jack++) {
75662306a36Sopenharmony_ci		unsigned int old_sense;
75762306a36Sopenharmony_ci		if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
75862306a36Sopenharmony_ci			continue;
75962306a36Sopenharmony_ci		old_sense = get_jack_plug_state(jack->pin_sense);
76062306a36Sopenharmony_ci		jack_detect_update(codec, jack);
76162306a36Sopenharmony_ci		if (old_sense == get_jack_plug_state(jack->pin_sense))
76262306a36Sopenharmony_ci			continue;
76362306a36Sopenharmony_ci		changes = 1;
76462306a36Sopenharmony_ci		call_jack_callback(codec, 0, jack);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	if (changes)
76762306a36Sopenharmony_ci		snd_hda_jack_report_sync(codec);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);
77062306a36Sopenharmony_ci
771