162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2014-2015 Intel Corp
662306a36Sopenharmony_ci *  Author: Samreen Nilofer <samreen.nilofer@intel.com>
762306a36Sopenharmony_ci *	    Subhransu S. Prusty <subhransu.s.prusty@intel.com>
862306a36Sopenharmony_ci *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/hdmi.h>
1862306a36Sopenharmony_ci#include <drm/drm_edid.h>
1962306a36Sopenharmony_ci#include <sound/pcm_params.h>
2062306a36Sopenharmony_ci#include <sound/jack.h>
2162306a36Sopenharmony_ci#include <sound/soc.h>
2262306a36Sopenharmony_ci#include <sound/hdaudio_ext.h>
2362306a36Sopenharmony_ci#include <sound/hda_i915.h>
2462306a36Sopenharmony_ci#include <sound/pcm_drm_eld.h>
2562306a36Sopenharmony_ci#include <sound/hda_chmap.h>
2662306a36Sopenharmony_ci#include "../../hda/local.h"
2762306a36Sopenharmony_ci#include "hdac_hdmi.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define NAME_SIZE	32
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define AMP_OUT_MUTE		0xb080
3262306a36Sopenharmony_ci#define AMP_OUT_UNMUTE		0xb000
3362306a36Sopenharmony_ci#define PIN_OUT			(AC_PINCTL_OUT_EN)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define HDA_MAX_CONNECTIONS     32
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define HDA_MAX_CVTS		3
3862306a36Sopenharmony_ci#define HDA_MAX_PORTS		3
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define ELD_MAX_SIZE    256
4162306a36Sopenharmony_ci#define ELD_FIXED_BYTES	20
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define ELD_VER_CEA_861D 2
4462306a36Sopenharmony_ci#define ELD_VER_PARTIAL 31
4562306a36Sopenharmony_ci#define ELD_MAX_MNL     16
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct hdac_hdmi_cvt_params {
4862306a36Sopenharmony_ci	unsigned int channels_min;
4962306a36Sopenharmony_ci	unsigned int channels_max;
5062306a36Sopenharmony_ci	u32 rates;
5162306a36Sopenharmony_ci	u64 formats;
5262306a36Sopenharmony_ci	unsigned int maxbps;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct hdac_hdmi_cvt {
5662306a36Sopenharmony_ci	struct list_head head;
5762306a36Sopenharmony_ci	hda_nid_t nid;
5862306a36Sopenharmony_ci	const char *name;
5962306a36Sopenharmony_ci	struct hdac_hdmi_cvt_params params;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Currently only spk_alloc, more to be added */
6362306a36Sopenharmony_cistruct hdac_hdmi_parsed_eld {
6462306a36Sopenharmony_ci	u8 spk_alloc;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistruct hdac_hdmi_eld {
6862306a36Sopenharmony_ci	bool	monitor_present;
6962306a36Sopenharmony_ci	bool	eld_valid;
7062306a36Sopenharmony_ci	int	eld_size;
7162306a36Sopenharmony_ci	char    eld_buffer[ELD_MAX_SIZE];
7262306a36Sopenharmony_ci	struct	hdac_hdmi_parsed_eld info;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct hdac_hdmi_pin {
7662306a36Sopenharmony_ci	struct list_head head;
7762306a36Sopenharmony_ci	hda_nid_t nid;
7862306a36Sopenharmony_ci	bool mst_capable;
7962306a36Sopenharmony_ci	struct hdac_hdmi_port *ports;
8062306a36Sopenharmony_ci	int num_ports;
8162306a36Sopenharmony_ci	struct hdac_device *hdev;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct hdac_hdmi_port {
8562306a36Sopenharmony_ci	struct list_head head;
8662306a36Sopenharmony_ci	int id;
8762306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
8862306a36Sopenharmony_ci	int num_mux_nids;
8962306a36Sopenharmony_ci	hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
9062306a36Sopenharmony_ci	struct hdac_hdmi_eld eld;
9162306a36Sopenharmony_ci	const char *jack_pin;
9262306a36Sopenharmony_ci	bool is_connect;
9362306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm;
9462306a36Sopenharmony_ci	const char *output_pin;
9562306a36Sopenharmony_ci	struct work_struct dapm_work;
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistruct hdac_hdmi_pcm {
9962306a36Sopenharmony_ci	struct list_head head;
10062306a36Sopenharmony_ci	int pcm_id;
10162306a36Sopenharmony_ci	struct list_head port_list;
10262306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
10362306a36Sopenharmony_ci	struct snd_soc_jack *jack;
10462306a36Sopenharmony_ci	int stream_tag;
10562306a36Sopenharmony_ci	int channels;
10662306a36Sopenharmony_ci	int format;
10762306a36Sopenharmony_ci	bool chmap_set;
10862306a36Sopenharmony_ci	unsigned char chmap[8]; /* ALSA API channel-map */
10962306a36Sopenharmony_ci	struct mutex lock;
11062306a36Sopenharmony_ci	int jack_event;
11162306a36Sopenharmony_ci	struct snd_kcontrol *eld_ctl;
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistruct hdac_hdmi_dai_port_map {
11562306a36Sopenharmony_ci	int dai_id;
11662306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
11762306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct hdac_hdmi_drv_data {
12162306a36Sopenharmony_ci	unsigned int vendor_nid;
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct hdac_hdmi_priv {
12562306a36Sopenharmony_ci	struct hdac_device *hdev;
12662306a36Sopenharmony_ci	struct snd_soc_component *component;
12762306a36Sopenharmony_ci	struct snd_card *card;
12862306a36Sopenharmony_ci	struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS];
12962306a36Sopenharmony_ci	struct list_head pin_list;
13062306a36Sopenharmony_ci	struct list_head cvt_list;
13162306a36Sopenharmony_ci	struct list_head pcm_list;
13262306a36Sopenharmony_ci	int num_pin;
13362306a36Sopenharmony_ci	int num_cvt;
13462306a36Sopenharmony_ci	int num_ports;
13562306a36Sopenharmony_ci	struct mutex pin_mutex;
13662306a36Sopenharmony_ci	struct hdac_chmap chmap;
13762306a36Sopenharmony_ci	struct hdac_hdmi_drv_data *drv_data;
13862306a36Sopenharmony_ci	struct snd_soc_dai_driver *dai_drv;
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev)
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic struct hdac_hdmi_pcm *
14462306a36Sopenharmony_cihdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
14562306a36Sopenharmony_ci			   struct hdac_hdmi_cvt *cvt)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
15062306a36Sopenharmony_ci		if (pcm->cvt == cvt)
15162306a36Sopenharmony_ci			return pcm;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return NULL;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
15862306a36Sopenharmony_ci		struct hdac_hdmi_port *port, bool is_connect)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct hdac_device *hdev = port->pin->hdev;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	port->is_connect = is_connect;
16362306a36Sopenharmony_ci	if (is_connect) {
16462306a36Sopenharmony_ci		/*
16562306a36Sopenharmony_ci		 * Report Jack connect event when a device is connected
16662306a36Sopenharmony_ci		 * for the first time where same PCM is attached to multiple
16762306a36Sopenharmony_ci		 * ports.
16862306a36Sopenharmony_ci		 */
16962306a36Sopenharmony_ci		if (pcm->jack_event == 0) {
17062306a36Sopenharmony_ci			dev_dbg(&hdev->dev,
17162306a36Sopenharmony_ci					"jack report for pcm=%d\n",
17262306a36Sopenharmony_ci					pcm->pcm_id);
17362306a36Sopenharmony_ci			snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT,
17462306a36Sopenharmony_ci						SND_JACK_AVOUT);
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci		pcm->jack_event++;
17762306a36Sopenharmony_ci	} else {
17862306a36Sopenharmony_ci		/*
17962306a36Sopenharmony_ci		 * Report Jack disconnect event when a device is disconnected
18062306a36Sopenharmony_ci		 * is the only last connected device when same PCM is attached
18162306a36Sopenharmony_ci		 * to multiple ports.
18262306a36Sopenharmony_ci		 */
18362306a36Sopenharmony_ci		if (pcm->jack_event == 1)
18462306a36Sopenharmony_ci			snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT);
18562306a36Sopenharmony_ci		if (pcm->jack_event > 0)
18662306a36Sopenharmony_ci			pcm->jack_event--;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	if (port->is_connect)
19362306a36Sopenharmony_ci		snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
19462306a36Sopenharmony_ci	else
19562306a36Sopenharmony_ci		snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
19662306a36Sopenharmony_ci	snd_soc_dapm_sync(port->dapm);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void hdac_hdmi_jack_dapm_work(struct work_struct *work)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	port = container_of(work, struct hdac_hdmi_port, dapm_work);
20462306a36Sopenharmony_ci	hdac_hdmi_port_dapm_update(port);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm,
20862306a36Sopenharmony_ci		struct hdac_hdmi_port *port, bool is_connect)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	hdac_hdmi_jack_report(pcm, port, is_connect);
21162306a36Sopenharmony_ci	hdac_hdmi_port_dapm_update(port);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/* MST supported verbs */
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * Get the no devices that can be connected to a port on the Pin widget.
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_cistatic int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	unsigned int caps;
22162306a36Sopenharmony_ci	unsigned int type, param;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	caps = get_wcaps(hdev, nid);
22462306a36Sopenharmony_ci	type = get_wcaps_type(caps);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN))
22762306a36Sopenharmony_ci		return 0;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN);
23062306a36Sopenharmony_ci	if (param == -1)
23162306a36Sopenharmony_ci		return param;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return param & AC_DEV_LIST_LEN_MASK;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * Get the port entry select on the pin. Return the port entry
23862306a36Sopenharmony_ci * id selected on the pin. Return 0 means the first port entry
23962306a36Sopenharmony_ci * is selected or MST is not supported.
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_cistatic int hdac_hdmi_port_select_get(struct hdac_device *hdev,
24262306a36Sopenharmony_ci					struct hdac_hdmi_port *port)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	return snd_hdac_codec_read(hdev, port->pin->nid,
24562306a36Sopenharmony_ci				0, AC_VERB_GET_DEVICE_SEL, 0);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * Sets the selected port entry for the configuring Pin widget verb.
25062306a36Sopenharmony_ci * returns error if port set is not equal to port get otherwise success
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cistatic int hdac_hdmi_port_select_set(struct hdac_device *hdev,
25362306a36Sopenharmony_ci					struct hdac_hdmi_port *port)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int num_ports;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (!port->pin->mst_capable)
25862306a36Sopenharmony_ci		return 0;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* AC_PAR_DEVLIST_LEN is 0 based. */
26162306a36Sopenharmony_ci	num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid);
26262306a36Sopenharmony_ci	if (num_ports < 0)
26362306a36Sopenharmony_ci		return -EIO;
26462306a36Sopenharmony_ci	/*
26562306a36Sopenharmony_ci	 * Device List Length is a 0 based integer value indicating the
26662306a36Sopenharmony_ci	 * number of sink device that a MST Pin Widget can support.
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	if (num_ports + 1  < port->id)
26962306a36Sopenharmony_ci		return 0;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	snd_hdac_codec_write(hdev, port->pin->nid, 0,
27262306a36Sopenharmony_ci			AC_VERB_SET_DEVICE_SEL, port->id);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (port->id != hdac_hdmi_port_select_get(hdev, port))
27562306a36Sopenharmony_ci		return -EIO;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
28362306a36Sopenharmony_ci						int pcm_idx)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
28862306a36Sopenharmony_ci		if (pcm->pcm_id == pcm_idx)
28962306a36Sopenharmony_ci			return pcm;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return NULL;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic unsigned int sad_format(const u8 *sad)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	return ((sad[0] >> 0x3) & 0x1f);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic unsigned int sad_sample_bits_lpcm(const u8 *sad)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	return (sad[2] & 7);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
30662306a36Sopenharmony_ci						void *eld)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	u64 formats = SNDRV_PCM_FMTBIT_S16;
30962306a36Sopenharmony_ci	int i;
31062306a36Sopenharmony_ci	const u8 *sad, *eld_buf = eld;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	sad = drm_eld_sad(eld_buf);
31362306a36Sopenharmony_ci	if (!sad)
31462306a36Sopenharmony_ci		goto format_constraint;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
31762306a36Sopenharmony_ci		if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci			/*
32062306a36Sopenharmony_ci			 * the controller support 20 and 24 bits in 32 bit
32162306a36Sopenharmony_ci			 * container so we set S32
32262306a36Sopenharmony_ci			 */
32362306a36Sopenharmony_ci			if (sad_sample_bits_lpcm(sad) & 0x6)
32462306a36Sopenharmony_ci				formats |= SNDRV_PCM_FMTBIT_S32;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ciformat_constraint:
32962306a36Sopenharmony_ci	return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
33062306a36Sopenharmony_ci				formats);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void
33562306a36Sopenharmony_cihdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid,
33662306a36Sopenharmony_ci				int packet_index, int byte_index)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	int val;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	val = (packet_index << 5) | (byte_index & 0x1f);
34162306a36Sopenharmony_ci	snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistruct dp_audio_infoframe {
34562306a36Sopenharmony_ci	u8 type; /* 0x84 */
34662306a36Sopenharmony_ci	u8 len;  /* 0x1b */
34762306a36Sopenharmony_ci	u8 ver;  /* 0x11 << 2 */
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	u8 CC02_CT47;	/* match with HDMI infoframe from this on */
35062306a36Sopenharmony_ci	u8 SS01_SF24;
35162306a36Sopenharmony_ci	u8 CXT04;
35262306a36Sopenharmony_ci	u8 CA;
35362306a36Sopenharmony_ci	u8 LFEPBL01_LSV36_DM_INH7;
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev,
35762306a36Sopenharmony_ci		   struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
36062306a36Sopenharmony_ci	struct hdmi_audio_infoframe frame;
36162306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin = port->pin;
36262306a36Sopenharmony_ci	struct dp_audio_infoframe dp_ai;
36362306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
36462306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt = pcm->cvt;
36562306a36Sopenharmony_ci	u8 *dip;
36662306a36Sopenharmony_ci	int ret;
36762306a36Sopenharmony_ci	int i;
36862306a36Sopenharmony_ci	const u8 *eld_buf;
36962306a36Sopenharmony_ci	u8 conn_type;
37062306a36Sopenharmony_ci	int channels, ca;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc,
37362306a36Sopenharmony_ci			pcm->channels, pcm->chmap_set, true, pcm->chmap);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	channels = snd_hdac_get_active_channels(ca);
37662306a36Sopenharmony_ci	hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
37962306a36Sopenharmony_ci				pcm->channels, pcm->chmap, pcm->chmap_set);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	eld_buf = port->eld.eld_buffer;
38262306a36Sopenharmony_ci	conn_type = drm_eld_get_conn_type(eld_buf);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	switch (conn_type) {
38562306a36Sopenharmony_ci	case DRM_ELD_CONN_TYPE_HDMI:
38662306a36Sopenharmony_ci		hdmi_audio_infoframe_init(&frame);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		frame.channels = channels;
38962306a36Sopenharmony_ci		frame.channel_allocation = ca;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
39262306a36Sopenharmony_ci		if (ret < 0)
39362306a36Sopenharmony_ci			return ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		break;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	case DRM_ELD_CONN_TYPE_DP:
39862306a36Sopenharmony_ci		memset(&dp_ai, 0, sizeof(dp_ai));
39962306a36Sopenharmony_ci		dp_ai.type	= 0x84;
40062306a36Sopenharmony_ci		dp_ai.len	= 0x1b;
40162306a36Sopenharmony_ci		dp_ai.ver	= 0x11 << 2;
40262306a36Sopenharmony_ci		dp_ai.CC02_CT47	= channels - 1;
40362306a36Sopenharmony_ci		dp_ai.CA	= ca;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		dip = (u8 *)&dp_ai;
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	default:
40962306a36Sopenharmony_ci		dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type);
41062306a36Sopenharmony_ci		return -EIO;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* stop infoframe transmission */
41462306a36Sopenharmony_ci	hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
41562306a36Sopenharmony_ci	snd_hdac_codec_write(hdev, pin->nid, 0,
41662306a36Sopenharmony_ci			AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/*  Fill infoframe. Index auto-incremented */
42062306a36Sopenharmony_ci	hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
42162306a36Sopenharmony_ci	if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
42262306a36Sopenharmony_ci		for (i = 0; i < sizeof(buffer); i++)
42362306a36Sopenharmony_ci			snd_hdac_codec_write(hdev, pin->nid, 0,
42462306a36Sopenharmony_ci				AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);
42562306a36Sopenharmony_ci	} else {
42662306a36Sopenharmony_ci		for (i = 0; i < sizeof(dp_ai); i++)
42762306a36Sopenharmony_ci			snd_hdac_codec_write(hdev, pin->nid, 0,
42862306a36Sopenharmony_ci				AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Start infoframe */
43262306a36Sopenharmony_ci	hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0);
43362306a36Sopenharmony_ci	snd_hdac_codec_write(hdev, pin->nid, 0,
43462306a36Sopenharmony_ci			AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int hdac_hdmi_set_stream(struct snd_soc_dai *dai,
44062306a36Sopenharmony_ci				void *stream, int direction)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
44362306a36Sopenharmony_ci	struct hdac_device *hdev = hdmi->hdev;
44462306a36Sopenharmony_ci	struct hdac_hdmi_dai_port_map *dai_map;
44562306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
44662306a36Sopenharmony_ci	struct hdac_stream *hstream;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (!stream)
44962306a36Sopenharmony_ci		return -EINVAL;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	hstream = (struct hdac_stream *)stream;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, hstream->stream_tag);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	dai_map = &hdmi->dai_map[dai->id];
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (pcm)
46062306a36Sopenharmony_ci		pcm->stream_tag = (hstream->stream_tag << 4);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return 0;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
46662306a36Sopenharmony_ci	struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
46962306a36Sopenharmony_ci	struct hdac_hdmi_dai_port_map *dai_map;
47062306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
47162306a36Sopenharmony_ci	int format;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dai_map = &hdmi->dai_map[dai->id];
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	format = snd_hdac_calc_stream_format(params_rate(hparams),
47662306a36Sopenharmony_ci			params_channels(hparams), params_format(hparams),
47762306a36Sopenharmony_ci			dai->driver->playback.sig_bits, 0);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
48062306a36Sopenharmony_ci	if (!pcm)
48162306a36Sopenharmony_ci		return -EIO;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	pcm->format = format;
48462306a36Sopenharmony_ci	pcm->channels = params_channels(hparams);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int hdac_hdmi_query_port_connlist(struct hdac_device *hdev,
49062306a36Sopenharmony_ci					struct hdac_hdmi_pin *pin,
49162306a36Sopenharmony_ci					struct hdac_hdmi_port *port)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) {
49462306a36Sopenharmony_ci		dev_warn(&hdev->dev,
49562306a36Sopenharmony_ci			"HDMI: pin %d wcaps %#x does not support connection list\n",
49662306a36Sopenharmony_ci			pin->nid, get_wcaps(hdev, pin->nid));
49762306a36Sopenharmony_ci		return -EINVAL;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (hdac_hdmi_port_select_set(hdev, port) < 0)
50162306a36Sopenharmony_ci		return -EIO;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid,
50462306a36Sopenharmony_ci			port->mux_nids, HDA_MAX_CONNECTIONS);
50562306a36Sopenharmony_ci	if (port->num_mux_nids == 0)
50662306a36Sopenharmony_ci		dev_warn(&hdev->dev,
50762306a36Sopenharmony_ci			"No connections found for pin:port %d:%d\n",
50862306a36Sopenharmony_ci						pin->nid, port->id);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n",
51162306a36Sopenharmony_ci			port->num_mux_nids, pin->nid, port->id);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return port->num_mux_nids;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci/*
51762306a36Sopenharmony_ci * Query pcm list and return port to which stream is routed.
51862306a36Sopenharmony_ci *
51962306a36Sopenharmony_ci * Also query connection list of the pin, to validate the cvt to port map.
52062306a36Sopenharmony_ci *
52162306a36Sopenharmony_ci * Same stream rendering to multiple ports simultaneously can be done
52262306a36Sopenharmony_ci * possibly, but not supported for now in driver. So return the first port
52362306a36Sopenharmony_ci * connected.
52462306a36Sopenharmony_ci */
52562306a36Sopenharmony_cistatic struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
52662306a36Sopenharmony_ci			struct hdac_device *hdev,
52762306a36Sopenharmony_ci			struct hdac_hdmi_priv *hdmi,
52862306a36Sopenharmony_ci			struct hdac_hdmi_cvt *cvt)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
53162306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
53262306a36Sopenharmony_ci	int ret, i;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
53562306a36Sopenharmony_ci		if (pcm->cvt == cvt) {
53662306a36Sopenharmony_ci			if (list_empty(&pcm->port_list))
53762306a36Sopenharmony_ci				continue;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci			list_for_each_entry(port, &pcm->port_list, head) {
54062306a36Sopenharmony_ci				mutex_lock(&pcm->lock);
54162306a36Sopenharmony_ci				ret = hdac_hdmi_query_port_connlist(hdev,
54262306a36Sopenharmony_ci							port->pin, port);
54362306a36Sopenharmony_ci				mutex_unlock(&pcm->lock);
54462306a36Sopenharmony_ci				if (ret < 0)
54562306a36Sopenharmony_ci					continue;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci				for (i = 0; i < port->num_mux_nids; i++) {
54862306a36Sopenharmony_ci					if (port->mux_nids[i] == cvt->nid &&
54962306a36Sopenharmony_ci						port->eld.monitor_present &&
55062306a36Sopenharmony_ci						port->eld.eld_valid)
55162306a36Sopenharmony_ci						return port;
55262306a36Sopenharmony_ci				}
55362306a36Sopenharmony_ci			}
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return NULL;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci/*
56162306a36Sopenharmony_ci * Go through all converters and ensure connection is set to
56262306a36Sopenharmony_ci * the correct pin as set via kcontrols.
56362306a36Sopenharmony_ci */
56462306a36Sopenharmony_cistatic void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
56762306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
56862306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
56962306a36Sopenharmony_ci	int cvt_idx = 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
57262306a36Sopenharmony_ci		port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
57362306a36Sopenharmony_ci		if (port && port->pin) {
57462306a36Sopenharmony_ci			snd_hdac_codec_write(hdev, port->pin->nid, 0,
57562306a36Sopenharmony_ci					     AC_VERB_SET_CONNECT_SEL, cvt_idx);
57662306a36Sopenharmony_ci			dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n",
57762306a36Sopenharmony_ci				__func__, cvt->name, port->pin->nid, cvt_idx);
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci		++cvt_idx;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/*
58462306a36Sopenharmony_ci * This tries to get a valid pin and set the HW constraints based on the
58562306a36Sopenharmony_ci * ELD. Even if a valid pin is not found return success so that device open
58662306a36Sopenharmony_ci * doesn't fail.
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_cistatic int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
58962306a36Sopenharmony_ci			struct snd_soc_dai *dai)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
59262306a36Sopenharmony_ci	struct hdac_device *hdev = hdmi->hdev;
59362306a36Sopenharmony_ci	struct hdac_hdmi_dai_port_map *dai_map;
59462306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
59562306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
59662306a36Sopenharmony_ci	int ret;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	dai_map = &hdmi->dai_map[dai->id];
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	cvt = dai_map->cvt;
60162306a36Sopenharmony_ci	port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/*
60462306a36Sopenharmony_ci	 * To make PA and other userland happy.
60562306a36Sopenharmony_ci	 * userland scans devices so returning error does not help.
60662306a36Sopenharmony_ci	 */
60762306a36Sopenharmony_ci	if (!port)
60862306a36Sopenharmony_ci		return 0;
60962306a36Sopenharmony_ci	if ((!port->eld.monitor_present) ||
61062306a36Sopenharmony_ci			(!port->eld.eld_valid)) {
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		dev_warn(&hdev->dev,
61362306a36Sopenharmony_ci			"Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n",
61462306a36Sopenharmony_ci			port->eld.monitor_present, port->eld.eld_valid,
61562306a36Sopenharmony_ci			port->pin->nid, port->id);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		return 0;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	dai_map->port = port;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	ret = hdac_hdmi_eld_limit_formats(substream->runtime,
62362306a36Sopenharmony_ci				port->eld.eld_buffer);
62462306a36Sopenharmony_ci	if (ret < 0)
62562306a36Sopenharmony_ci		return ret;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return snd_pcm_hw_constraint_eld(substream->runtime,
62862306a36Sopenharmony_ci				port->eld.eld_buffer);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
63262306a36Sopenharmony_ci		struct snd_soc_dai *dai)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
63562306a36Sopenharmony_ci	struct hdac_hdmi_dai_port_map *dai_map;
63662306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	dai_map = &hdmi->dai_map[dai->id];
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (pcm) {
64362306a36Sopenharmony_ci		mutex_lock(&pcm->lock);
64462306a36Sopenharmony_ci		pcm->chmap_set = false;
64562306a36Sopenharmony_ci		memset(pcm->chmap, 0, sizeof(pcm->chmap));
64662306a36Sopenharmony_ci		pcm->channels = 0;
64762306a36Sopenharmony_ci		mutex_unlock(&pcm->lock);
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (dai_map->port)
65162306a36Sopenharmony_ci		dai_map->port = NULL;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic int
65562306a36Sopenharmony_cihdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	unsigned int chans;
65862306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
65962306a36Sopenharmony_ci	int err;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	chans = get_wcaps(hdev, cvt->nid);
66262306a36Sopenharmony_ci	chans = get_wcaps_channels(chans);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	cvt->params.channels_min = 2;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	cvt->params.channels_max = chans;
66762306a36Sopenharmony_ci	if (chans > hdmi->chmap.channels_max)
66862306a36Sopenharmony_ci		hdmi->chmap.channels_max = chans;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	err = snd_hdac_query_supported_pcm(hdev, cvt->nid,
67162306a36Sopenharmony_ci			&cvt->params.rates,
67262306a36Sopenharmony_ci			&cvt->params.formats,
67362306a36Sopenharmony_ci			&cvt->params.maxbps);
67462306a36Sopenharmony_ci	if (err < 0)
67562306a36Sopenharmony_ci		dev_err(&hdev->dev,
67662306a36Sopenharmony_ci			"Failed to query pcm params for nid %d: %d\n",
67762306a36Sopenharmony_ci			cvt->nid, err);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return err;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int hdac_hdmi_fill_widget_info(struct device *dev,
68362306a36Sopenharmony_ci		struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
68462306a36Sopenharmony_ci		void *priv, const char *wname, const char *stream,
68562306a36Sopenharmony_ci		struct snd_kcontrol_new *wc, int numkc,
68662306a36Sopenharmony_ci		int (*event)(struct snd_soc_dapm_widget *,
68762306a36Sopenharmony_ci		struct snd_kcontrol *, int), unsigned short event_flags)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	w->id = id;
69062306a36Sopenharmony_ci	w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
69162306a36Sopenharmony_ci	if (!w->name)
69262306a36Sopenharmony_ci		return -ENOMEM;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	w->sname = stream;
69562306a36Sopenharmony_ci	w->reg = SND_SOC_NOPM;
69662306a36Sopenharmony_ci	w->shift = 0;
69762306a36Sopenharmony_ci	w->kcontrol_news = wc;
69862306a36Sopenharmony_ci	w->num_kcontrols = numkc;
69962306a36Sopenharmony_ci	w->priv = priv;
70062306a36Sopenharmony_ci	w->event = event;
70162306a36Sopenharmony_ci	w->event_flags = event_flags;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
70762306a36Sopenharmony_ci		const char *sink, const char *control, const char *src,
70862306a36Sopenharmony_ci		int (*handler)(struct snd_soc_dapm_widget *src,
70962306a36Sopenharmony_ci			struct snd_soc_dapm_widget *sink))
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	route->sink = sink;
71262306a36Sopenharmony_ci	route->source = src;
71362306a36Sopenharmony_ci	route->control = control;
71462306a36Sopenharmony_ci	route->connected = handler;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev,
71862306a36Sopenharmony_ci					struct hdac_hdmi_port *port)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
72162306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
72262306a36Sopenharmony_ci	struct hdac_hdmi_port *p;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
72562306a36Sopenharmony_ci		if (list_empty(&pcm->port_list))
72662306a36Sopenharmony_ci			continue;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		list_for_each_entry(p, &pcm->port_list, head) {
72962306a36Sopenharmony_ci			if (p->id == port->id && port->pin == p->pin)
73062306a36Sopenharmony_ci				return pcm;
73162306a36Sopenharmony_ci		}
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return NULL;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic void hdac_hdmi_set_power_state(struct hdac_device *hdev,
73862306a36Sopenharmony_ci			     hda_nid_t nid, unsigned int pwr_state)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	int count;
74162306a36Sopenharmony_ci	unsigned int state;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (get_wcaps(hdev, nid) & AC_WCAP_POWER) {
74462306a36Sopenharmony_ci		if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) {
74562306a36Sopenharmony_ci			for (count = 0; count < 10; count++) {
74662306a36Sopenharmony_ci				snd_hdac_codec_read(hdev, nid, 0,
74762306a36Sopenharmony_ci						AC_VERB_SET_POWER_STATE,
74862306a36Sopenharmony_ci						pwr_state);
74962306a36Sopenharmony_ci				state = snd_hdac_sync_power_state(hdev,
75062306a36Sopenharmony_ci						nid, pwr_state);
75162306a36Sopenharmony_ci				if (!(state & AC_PWRST_ERROR))
75262306a36Sopenharmony_ci					break;
75362306a36Sopenharmony_ci			}
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic void hdac_hdmi_set_amp(struct hdac_device *hdev,
75962306a36Sopenharmony_ci				   hda_nid_t nid, int val)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP)
76262306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, nid, 0,
76362306a36Sopenharmony_ci					AC_VERB_SET_AMP_GAIN_MUTE, val);
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w,
76862306a36Sopenharmony_ci					struct snd_kcontrol *kc, int event)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct hdac_hdmi_port *port = w->priv;
77162306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
77262306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
77562306a36Sopenharmony_ci			__func__, w->name, event);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	pcm = hdac_hdmi_get_pcm(hdev, port);
77862306a36Sopenharmony_ci	if (!pcm)
77962306a36Sopenharmony_ci		return -EIO;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	/* set the device if pin is mst_capable */
78262306a36Sopenharmony_ci	if (hdac_hdmi_port_select_set(hdev, port) < 0)
78362306a36Sopenharmony_ci		return -EIO;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	switch (event) {
78662306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
78762306a36Sopenharmony_ci		hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		/* Enable out path for this pin widget */
79062306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, port->pin->nid, 0,
79162306a36Sopenharmony_ci				AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	case SND_SOC_DAPM_POST_PMD:
79862306a36Sopenharmony_ci		hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		/* Disable out path for this pin widget */
80162306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, port->pin->nid, 0,
80262306a36Sopenharmony_ci				AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3);
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return 0;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
81362306a36Sopenharmony_ci					struct snd_kcontrol *kc, int event)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt = w->priv;
81662306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
81762306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
81862306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
82162306a36Sopenharmony_ci			__func__, w->name, event);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
82462306a36Sopenharmony_ci	if (!pcm)
82562306a36Sopenharmony_ci		return -EIO;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	switch (event) {
82862306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
82962306a36Sopenharmony_ci		hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		/* Enable transmission */
83262306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, cvt->nid, 0,
83362306a36Sopenharmony_ci			AC_VERB_SET_DIGI_CONVERT_1, 1);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		/* Category Code (CC) to zero */
83662306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, cvt->nid, 0,
83762306a36Sopenharmony_ci			AC_VERB_SET_DIGI_CONVERT_2, 0);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, cvt->nid, 0,
84062306a36Sopenharmony_ci				AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
84162306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, cvt->nid, 0,
84262306a36Sopenharmony_ci				AC_VERB_SET_STREAM_FORMAT, pcm->format);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		/*
84562306a36Sopenharmony_ci		 * The connection indices are shared by all converters and
84662306a36Sopenharmony_ci		 * may interfere with each other. Ensure correct
84762306a36Sopenharmony_ci		 * routing for all converters at stream start.
84862306a36Sopenharmony_ci		 */
84962306a36Sopenharmony_ci		hdac_hdmi_verify_connect_sel_all_pins(hdev);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	case SND_SOC_DAPM_POST_PMD:
85462306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, cvt->nid, 0,
85562306a36Sopenharmony_ci				AC_VERB_SET_CHANNEL_STREAMID, 0);
85662306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, cvt->nid, 0,
85762306a36Sopenharmony_ci				AC_VERB_SET_STREAM_FORMAT, 0);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3);
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	return 0;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
86862306a36Sopenharmony_ci					struct snd_kcontrol *kc, int event)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct hdac_hdmi_port *port = w->priv;
87162306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev);
87262306a36Sopenharmony_ci	int mux_idx;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n",
87562306a36Sopenharmony_ci			__func__, w->name, event);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!kc)
87862306a36Sopenharmony_ci		kc  = w->kcontrols[0];
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	mux_idx = dapm_kcontrol_get_value(kc);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* set the device if pin is mst_capable */
88362306a36Sopenharmony_ci	if (hdac_hdmi_port_select_set(hdev, port) < 0)
88462306a36Sopenharmony_ci		return -EIO;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (mux_idx > 0) {
88762306a36Sopenharmony_ci		snd_hdac_codec_write(hdev, port->pin->nid, 0,
88862306a36Sopenharmony_ci			AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	return 0;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci/*
89562306a36Sopenharmony_ci * Based on user selection, map the PINs with the PCMs.
89662306a36Sopenharmony_ci */
89762306a36Sopenharmony_cistatic int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
89862306a36Sopenharmony_ci		struct snd_ctl_elem_value *ucontrol)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	int ret;
90162306a36Sopenharmony_ci	struct hdac_hdmi_port *p, *p_next;
90262306a36Sopenharmony_ci	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
90362306a36Sopenharmony_ci	struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
90462306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm = w->dapm;
90562306a36Sopenharmony_ci	struct hdac_hdmi_port *port = w->priv;
90662306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev);
90762306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
90862306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
90962306a36Sopenharmony_ci	const char *cvt_name =  e->texts[ucontrol->value.enumerated.item[0]];
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
91262306a36Sopenharmony_ci	if (ret < 0)
91362306a36Sopenharmony_ci		return ret;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (port == NULL)
91662306a36Sopenharmony_ci		return -EINVAL;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	mutex_lock(&hdmi->pin_mutex);
91962306a36Sopenharmony_ci	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
92062306a36Sopenharmony_ci		if (list_empty(&pcm->port_list))
92162306a36Sopenharmony_ci			continue;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci		list_for_each_entry_safe(p, p_next, &pcm->port_list, head) {
92462306a36Sopenharmony_ci			if (p == port && p->id == port->id &&
92562306a36Sopenharmony_ci					p->pin == port->pin) {
92662306a36Sopenharmony_ci				hdac_hdmi_jack_report_sync(pcm, port, false);
92762306a36Sopenharmony_ci				list_del(&p->head);
92862306a36Sopenharmony_ci			}
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/*
93362306a36Sopenharmony_ci	 * Jack status is not reported during device probe as the
93462306a36Sopenharmony_ci	 * PCMs are not registered by then. So report it here.
93562306a36Sopenharmony_ci	 */
93662306a36Sopenharmony_ci	list_for_each_entry(pcm, &hdmi->pcm_list, head) {
93762306a36Sopenharmony_ci		if (!strcmp(cvt_name, pcm->cvt->name)) {
93862306a36Sopenharmony_ci			list_add_tail(&port->head, &pcm->port_list);
93962306a36Sopenharmony_ci			if (port->eld.monitor_present && port->eld.eld_valid) {
94062306a36Sopenharmony_ci				hdac_hdmi_jack_report_sync(pcm, port, true);
94162306a36Sopenharmony_ci				mutex_unlock(&hdmi->pin_mutex);
94262306a36Sopenharmony_ci				return ret;
94362306a36Sopenharmony_ci			}
94462306a36Sopenharmony_ci		}
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci	mutex_unlock(&hdmi->pin_mutex);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return ret;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci/*
95262306a36Sopenharmony_ci * Ideally the Mux inputs should be based on the num_muxs enumerated, but
95362306a36Sopenharmony_ci * the display driver seem to be programming the connection list for the pin
95462306a36Sopenharmony_ci * widget runtime.
95562306a36Sopenharmony_ci *
95662306a36Sopenharmony_ci * So programming all the possible inputs for the mux, the user has to take
95762306a36Sopenharmony_ci * care of selecting the right one and leaving all other inputs selected to
95862306a36Sopenharmony_ci * "NONE"
95962306a36Sopenharmony_ci */
96062306a36Sopenharmony_cistatic int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev,
96162306a36Sopenharmony_ci				struct hdac_hdmi_port *port,
96262306a36Sopenharmony_ci				struct snd_soc_dapm_widget *widget,
96362306a36Sopenharmony_ci				const char *widget_name)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
96662306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin = port->pin;
96762306a36Sopenharmony_ci	struct snd_kcontrol_new *kc;
96862306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
96962306a36Sopenharmony_ci	struct soc_enum *se;
97062306a36Sopenharmony_ci	char kc_name[NAME_SIZE];
97162306a36Sopenharmony_ci	char mux_items[NAME_SIZE];
97262306a36Sopenharmony_ci	/* To hold inputs to the Pin mux */
97362306a36Sopenharmony_ci	char *items[HDA_MAX_CONNECTIONS];
97462306a36Sopenharmony_ci	int i = 0;
97562306a36Sopenharmony_ci	int num_items = hdmi->num_cvt + 1;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL);
97862306a36Sopenharmony_ci	if (!kc)
97962306a36Sopenharmony_ci		return -ENOMEM;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL);
98262306a36Sopenharmony_ci	if (!se)
98362306a36Sopenharmony_ci		return -ENOMEM;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input",
98662306a36Sopenharmony_ci						pin->nid, port->id);
98762306a36Sopenharmony_ci	kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL);
98862306a36Sopenharmony_ci	if (!kc->name)
98962306a36Sopenharmony_ci		return -ENOMEM;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	kc->private_value = (long)se;
99262306a36Sopenharmony_ci	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
99362306a36Sopenharmony_ci	kc->access = 0;
99462306a36Sopenharmony_ci	kc->info = snd_soc_info_enum_double;
99562306a36Sopenharmony_ci	kc->put = hdac_hdmi_set_pin_port_mux;
99662306a36Sopenharmony_ci	kc->get = snd_soc_dapm_get_enum_double;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	se->reg = SND_SOC_NOPM;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	/* enum texts: ["NONE", "cvt #", "cvt #", ...] */
100162306a36Sopenharmony_ci	se->items = num_items;
100262306a36Sopenharmony_ci	se->mask = roundup_pow_of_two(se->items) - 1;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	sprintf(mux_items, "NONE");
100562306a36Sopenharmony_ci	items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
100662306a36Sopenharmony_ci	if (!items[i])
100762306a36Sopenharmony_ci		return -ENOMEM;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
101062306a36Sopenharmony_ci		i++;
101162306a36Sopenharmony_ci		sprintf(mux_items, "cvt %d", cvt->nid);
101262306a36Sopenharmony_ci		items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL);
101362306a36Sopenharmony_ci		if (!items[i])
101462306a36Sopenharmony_ci			return -ENOMEM;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	se->texts = devm_kmemdup(&hdev->dev, items,
101862306a36Sopenharmony_ci			(num_items  * sizeof(char *)), GFP_KERNEL);
101962306a36Sopenharmony_ci	if (!se->texts)
102062306a36Sopenharmony_ci		return -ENOMEM;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	return hdac_hdmi_fill_widget_info(&hdev->dev, widget,
102362306a36Sopenharmony_ci			snd_soc_dapm_mux, port, widget_name, NULL, kc, 1,
102462306a36Sopenharmony_ci			hdac_hdmi_pin_mux_widget_event,
102562306a36Sopenharmony_ci			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci/* Add cvt <- input <- mux route map */
102962306a36Sopenharmony_cistatic void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev,
103062306a36Sopenharmony_ci			struct snd_soc_dapm_widget *widgets,
103162306a36Sopenharmony_ci			struct snd_soc_dapm_route *route, int rindex)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
103462306a36Sopenharmony_ci	const struct snd_kcontrol_new *kc;
103562306a36Sopenharmony_ci	struct soc_enum *se;
103662306a36Sopenharmony_ci	int mux_index = hdmi->num_cvt + hdmi->num_ports;
103762306a36Sopenharmony_ci	int i, j;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	for (i = 0; i < hdmi->num_ports; i++) {
104062306a36Sopenharmony_ci		kc = widgets[mux_index].kcontrol_news;
104162306a36Sopenharmony_ci		se = (struct soc_enum *)kc->private_value;
104262306a36Sopenharmony_ci		for (j = 0; j < hdmi->num_cvt; j++) {
104362306a36Sopenharmony_ci			hdac_hdmi_fill_route(&route[rindex],
104462306a36Sopenharmony_ci					widgets[mux_index].name,
104562306a36Sopenharmony_ci					se->texts[j + 1],
104662306a36Sopenharmony_ci					widgets[j].name, NULL);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci			rindex++;
104962306a36Sopenharmony_ci		}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci		mux_index++;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci/*
105662306a36Sopenharmony_ci * Widgets are added in the below sequence
105762306a36Sopenharmony_ci *	Converter widgets for num converters enumerated
105862306a36Sopenharmony_ci *	Pin-port widgets for num ports for Pins enumerated
105962306a36Sopenharmony_ci *	Pin-port mux widgets to represent connenction list of pin widget
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci * For each port, one Mux and One output widget is added
106262306a36Sopenharmony_ci * Total widgets elements = num_cvt + (num_ports * 2);
106362306a36Sopenharmony_ci *
106462306a36Sopenharmony_ci * Routes are added as below:
106562306a36Sopenharmony_ci *	pin-port mux -> pin (based on num_ports)
106662306a36Sopenharmony_ci *	cvt -> "Input sel control" -> pin-port_mux
106762306a36Sopenharmony_ci *
106862306a36Sopenharmony_ci * Total route elements:
106962306a36Sopenharmony_ci *	num_ports + (pin_muxes * num_cvt)
107062306a36Sopenharmony_ci */
107162306a36Sopenharmony_cistatic int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct snd_soc_dapm_widget *widgets;
107462306a36Sopenharmony_ci	struct snd_soc_dapm_route *route;
107562306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev);
107662306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
107762306a36Sopenharmony_ci	struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv;
107862306a36Sopenharmony_ci	char widget_name[NAME_SIZE];
107962306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
108062306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
108162306a36Sopenharmony_ci	int ret, i = 0, num_routes = 0, j;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
108462306a36Sopenharmony_ci		return -EINVAL;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) *
108762306a36Sopenharmony_ci				((2 * hdmi->num_ports) + hdmi->num_cvt)),
108862306a36Sopenharmony_ci				GFP_KERNEL);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (!widgets)
109162306a36Sopenharmony_ci		return -ENOMEM;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	/* DAPM widgets to represent each converter widget */
109462306a36Sopenharmony_ci	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
109562306a36Sopenharmony_ci		sprintf(widget_name, "Converter %d", cvt->nid);
109662306a36Sopenharmony_ci		ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
109762306a36Sopenharmony_ci			snd_soc_dapm_aif_in, cvt,
109862306a36Sopenharmony_ci			widget_name, dai_drv[i].playback.stream_name, NULL, 0,
109962306a36Sopenharmony_ci			hdac_hdmi_cvt_output_widget_event,
110062306a36Sopenharmony_ci			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
110162306a36Sopenharmony_ci		if (ret < 0)
110262306a36Sopenharmony_ci			return ret;
110362306a36Sopenharmony_ci		i++;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
110762306a36Sopenharmony_ci		for (j = 0; j < pin->num_ports; j++) {
110862306a36Sopenharmony_ci			sprintf(widget_name, "hif%d-%d Output",
110962306a36Sopenharmony_ci				pin->nid, pin->ports[j].id);
111062306a36Sopenharmony_ci			ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
111162306a36Sopenharmony_ci					snd_soc_dapm_output, &pin->ports[j],
111262306a36Sopenharmony_ci					widget_name, NULL, NULL, 0,
111362306a36Sopenharmony_ci					hdac_hdmi_pin_output_widget_event,
111462306a36Sopenharmony_ci					SND_SOC_DAPM_PRE_PMU |
111562306a36Sopenharmony_ci					SND_SOC_DAPM_POST_PMD);
111662306a36Sopenharmony_ci			if (ret < 0)
111762306a36Sopenharmony_ci				return ret;
111862306a36Sopenharmony_ci			pin->ports[j].output_pin = widgets[i].name;
111962306a36Sopenharmony_ci			i++;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/* DAPM widgets to represent the connection list to pin widget */
112462306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
112562306a36Sopenharmony_ci		for (j = 0; j < pin->num_ports; j++) {
112662306a36Sopenharmony_ci			sprintf(widget_name, "Pin%d-Port%d Mux",
112762306a36Sopenharmony_ci				pin->nid, pin->ports[j].id);
112862306a36Sopenharmony_ci			ret = hdac_hdmi_create_pin_port_muxs(hdev,
112962306a36Sopenharmony_ci						&pin->ports[j], &widgets[i],
113062306a36Sopenharmony_ci						widget_name);
113162306a36Sopenharmony_ci			if (ret < 0)
113262306a36Sopenharmony_ci				return ret;
113362306a36Sopenharmony_ci			i++;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci			/* For cvt to pin_mux mapping */
113662306a36Sopenharmony_ci			num_routes += hdmi->num_cvt;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci			/* For pin_mux to pin mapping */
113962306a36Sopenharmony_ci			num_routes++;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
114462306a36Sopenharmony_ci							GFP_KERNEL);
114562306a36Sopenharmony_ci	if (!route)
114662306a36Sopenharmony_ci		return -ENOMEM;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	i = 0;
114962306a36Sopenharmony_ci	/* Add pin <- NULL <- mux route map */
115062306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
115162306a36Sopenharmony_ci		for (j = 0; j < pin->num_ports; j++) {
115262306a36Sopenharmony_ci			int sink_index = i + hdmi->num_cvt;
115362306a36Sopenharmony_ci			int src_index = sink_index + pin->num_ports *
115462306a36Sopenharmony_ci						hdmi->num_pin;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci			hdac_hdmi_fill_route(&route[i],
115762306a36Sopenharmony_ci				widgets[sink_index].name, NULL,
115862306a36Sopenharmony_ci				widgets[src_index].name, NULL);
115962306a36Sopenharmony_ci			i++;
116062306a36Sopenharmony_ci		}
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	snd_soc_dapm_new_controls(dapm, widgets,
116662306a36Sopenharmony_ci		((2 * hdmi->num_ports) + hdmi->num_cvt));
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	snd_soc_dapm_add_routes(dapm, route, num_routes);
116962306a36Sopenharmony_ci	snd_soc_dapm_new_widgets(dapm->card);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	return 0;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_cistatic int hdac_hdmi_init_dai_map(struct hdac_device *hdev)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
117862306a36Sopenharmony_ci	struct hdac_hdmi_dai_port_map *dai_map;
117962306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
118062306a36Sopenharmony_ci	int dai_id = 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (list_empty(&hdmi->cvt_list))
118362306a36Sopenharmony_ci		return -EINVAL;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
118662306a36Sopenharmony_ci		dai_map = &hdmi->dai_map[dai_id];
118762306a36Sopenharmony_ci		dai_map->dai_id = dai_id;
118862306a36Sopenharmony_ci		dai_map->cvt = cvt;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		dai_id++;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		if (dai_id == HDA_MAX_CVTS) {
119362306a36Sopenharmony_ci			dev_warn(&hdev->dev,
119462306a36Sopenharmony_ci				"Max dais supported: %d\n", dai_id);
119562306a36Sopenharmony_ci			break;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return 0;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
120562306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
120662306a36Sopenharmony_ci	char name[NAME_SIZE];
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL);
120962306a36Sopenharmony_ci	if (!cvt)
121062306a36Sopenharmony_ci		return -ENOMEM;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	cvt->nid = nid;
121362306a36Sopenharmony_ci	sprintf(name, "cvt %d", cvt->nid);
121462306a36Sopenharmony_ci	cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL);
121562306a36Sopenharmony_ci	if (!cvt->name)
121662306a36Sopenharmony_ci		return -ENOMEM;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	list_add_tail(&cvt->head, &hdmi->cvt_list);
121962306a36Sopenharmony_ci	hdmi->num_cvt++;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	return hdac_hdmi_query_cvt_params(hdev, cvt);
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cistatic int hdac_hdmi_parse_eld(struct hdac_device *hdev,
122562306a36Sopenharmony_ci			struct hdac_hdmi_port *port)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	unsigned int ver, mnl;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK)
123062306a36Sopenharmony_ci						>> DRM_ELD_VER_SHIFT;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
123362306a36Sopenharmony_ci		dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver);
123462306a36Sopenharmony_ci		return -EINVAL;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] &
123862306a36Sopenharmony_ci		DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	if (mnl > ELD_MAX_MNL) {
124162306a36Sopenharmony_ci		dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl);
124262306a36Sopenharmony_ci		return -EINVAL;
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER];
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return 0;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cistatic void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
125162306a36Sopenharmony_ci				    struct hdac_hdmi_port *port)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct hdac_device *hdev = pin->hdev;
125462306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
125562306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
125662306a36Sopenharmony_ci	int size = 0;
125762306a36Sopenharmony_ci	int port_id = -1;
125862306a36Sopenharmony_ci	bool eld_valid, eld_changed;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	if (!hdmi)
126162306a36Sopenharmony_ci		return;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/*
126462306a36Sopenharmony_ci	 * In case of non MST pin, get_eld info API expectes port
126562306a36Sopenharmony_ci	 * to be -1.
126662306a36Sopenharmony_ci	 */
126762306a36Sopenharmony_ci	mutex_lock(&hdmi->pin_mutex);
126862306a36Sopenharmony_ci	port->eld.monitor_present = false;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	if (pin->mst_capable)
127162306a36Sopenharmony_ci		port_id = port->id;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id,
127462306a36Sopenharmony_ci				&port->eld.monitor_present,
127562306a36Sopenharmony_ci				port->eld.eld_buffer,
127662306a36Sopenharmony_ci				ELD_MAX_SIZE);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (size > 0) {
127962306a36Sopenharmony_ci		size = min(size, ELD_MAX_SIZE);
128062306a36Sopenharmony_ci		if (hdac_hdmi_parse_eld(hdev, port) < 0)
128162306a36Sopenharmony_ci			size = -EINVAL;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	eld_valid = port->eld.eld_valid;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (size > 0) {
128762306a36Sopenharmony_ci		port->eld.eld_valid = true;
128862306a36Sopenharmony_ci		port->eld.eld_size = size;
128962306a36Sopenharmony_ci	} else {
129062306a36Sopenharmony_ci		port->eld.eld_valid = false;
129162306a36Sopenharmony_ci		port->eld.eld_size = 0;
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	eld_changed = (eld_valid != port->eld.eld_valid);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	pcm = hdac_hdmi_get_pcm(hdev, port);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (!port->eld.monitor_present || !port->eld.eld_valid) {
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n",
130162306a36Sopenharmony_ci						__func__, pin->nid, port->id);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		/*
130462306a36Sopenharmony_ci		 * PCMs are not registered during device probe, so don't
130562306a36Sopenharmony_ci		 * report jack here. It will be done in usermode mux
130662306a36Sopenharmony_ci		 * control select.
130762306a36Sopenharmony_ci		 */
130862306a36Sopenharmony_ci		if (pcm) {
130962306a36Sopenharmony_ci			hdac_hdmi_jack_report(pcm, port, false);
131062306a36Sopenharmony_ci			schedule_work(&port->dapm_work);
131162306a36Sopenharmony_ci		}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		mutex_unlock(&hdmi->pin_mutex);
131462306a36Sopenharmony_ci		return;
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (port->eld.monitor_present && port->eld.eld_valid) {
131862306a36Sopenharmony_ci		if (pcm) {
131962306a36Sopenharmony_ci			hdac_hdmi_jack_report(pcm, port, true);
132062306a36Sopenharmony_ci			schedule_work(&port->dapm_work);
132162306a36Sopenharmony_ci		}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1,
132462306a36Sopenharmony_ci			  port->eld.eld_buffer, port->eld.eld_size, false);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci	mutex_unlock(&hdmi->pin_mutex);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (eld_changed && pcm)
133062306a36Sopenharmony_ci		snd_ctl_notify(hdmi->card,
133162306a36Sopenharmony_ci			       SNDRV_CTL_EVENT_MASK_VALUE |
133262306a36Sopenharmony_ci			       SNDRV_CTL_EVENT_MASK_INFO,
133362306a36Sopenharmony_ci			       &pcm->eld_ctl->id);
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int hdac_hdmi_add_ports(struct hdac_device *hdev,
133762306a36Sopenharmony_ci			       struct hdac_hdmi_pin *pin)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	struct hdac_hdmi_port *ports;
134062306a36Sopenharmony_ci	int max_ports = HDA_MAX_PORTS;
134162306a36Sopenharmony_ci	int i;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	/*
134462306a36Sopenharmony_ci	 * FIXME: max_port may vary for each platform, so pass this as
134562306a36Sopenharmony_ci	 * as driver data or query from i915 interface when this API is
134662306a36Sopenharmony_ci	 * implemented.
134762306a36Sopenharmony_ci	 */
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL);
135062306a36Sopenharmony_ci	if (!ports)
135162306a36Sopenharmony_ci		return -ENOMEM;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	for (i = 0; i < max_ports; i++) {
135462306a36Sopenharmony_ci		ports[i].id = i;
135562306a36Sopenharmony_ci		ports[i].pin = pin;
135662306a36Sopenharmony_ci		INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work);
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci	pin->ports = ports;
135962306a36Sopenharmony_ci	pin->num_ports = max_ports;
136062306a36Sopenharmony_ci	return 0;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
136662306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
136762306a36Sopenharmony_ci	int ret;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL);
137062306a36Sopenharmony_ci	if (!pin)
137162306a36Sopenharmony_ci		return -ENOMEM;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	pin->nid = nid;
137462306a36Sopenharmony_ci	pin->mst_capable = false;
137562306a36Sopenharmony_ci	pin->hdev = hdev;
137662306a36Sopenharmony_ci	ret = hdac_hdmi_add_ports(hdev, pin);
137762306a36Sopenharmony_ci	if (ret < 0)
137862306a36Sopenharmony_ci		return ret;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	list_add_tail(&pin->head, &hdmi->pin_list);
138162306a36Sopenharmony_ci	hdmi->num_pin++;
138262306a36Sopenharmony_ci	hdmi->num_ports += pin->num_ports;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	return 0;
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci#define INTEL_VENDOR_NID 0x08
138862306a36Sopenharmony_ci#define INTEL_GLK_VENDOR_NID 0x0b
138962306a36Sopenharmony_ci#define INTEL_GET_VENDOR_VERB 0xf81
139062306a36Sopenharmony_ci#define INTEL_SET_VENDOR_VERB 0x781
139162306a36Sopenharmony_ci#define INTEL_EN_DP12			0x02 /* enable DP 1.2 features */
139262306a36Sopenharmony_ci#define INTEL_EN_ALL_PIN_CVTS	0x01 /* enable 2nd & 3rd pins and convertors */
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	unsigned int vendor_param;
139762306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
139862306a36Sopenharmony_ci	unsigned int vendor_nid = hdmi->drv_data->vendor_nid;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
140162306a36Sopenharmony_ci				INTEL_GET_VENDOR_VERB, 0);
140262306a36Sopenharmony_ci	if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
140362306a36Sopenharmony_ci		return;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	vendor_param |= INTEL_EN_ALL_PIN_CVTS;
140662306a36Sopenharmony_ci	vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
140762306a36Sopenharmony_ci				INTEL_SET_VENDOR_VERB, vendor_param);
140862306a36Sopenharmony_ci	if (vendor_param == -1)
140962306a36Sopenharmony_ci		return;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	unsigned int vendor_param;
141562306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
141662306a36Sopenharmony_ci	unsigned int vendor_nid = hdmi->drv_data->vendor_nid;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
141962306a36Sopenharmony_ci				INTEL_GET_VENDOR_VERB, 0);
142062306a36Sopenharmony_ci	if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
142162306a36Sopenharmony_ci		return;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	/* enable DP1.2 mode */
142462306a36Sopenharmony_ci	vendor_param |= INTEL_EN_DP12;
142562306a36Sopenharmony_ci	vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0,
142662306a36Sopenharmony_ci				INTEL_SET_VENDOR_VERB, vendor_param);
142762306a36Sopenharmony_ci	if (vendor_param == -1)
142862306a36Sopenharmony_ci		return;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cistatic int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
143362306a36Sopenharmony_ci			     struct snd_ctl_elem_info *uinfo)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
143662306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
143762306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
143862306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
143962306a36Sopenharmony_ci	struct hdac_hdmi_eld *eld;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
144262306a36Sopenharmony_ci	uinfo->count = 0;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
144562306a36Sopenharmony_ci	if (!pcm) {
144662306a36Sopenharmony_ci		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
144762306a36Sopenharmony_ci			kcontrol->id.device);
144862306a36Sopenharmony_ci		return 0;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (list_empty(&pcm->port_list)) {
145262306a36Sopenharmony_ci		dev_dbg(component->dev, "%s: empty port list, device %d\n",
145362306a36Sopenharmony_ci			__func__, kcontrol->id.device);
145462306a36Sopenharmony_ci		return 0;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	mutex_lock(&hdmi->pin_mutex);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	list_for_each_entry(port, &pcm->port_list, head) {
146062306a36Sopenharmony_ci		eld = &port->eld;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci		if (eld->eld_valid) {
146362306a36Sopenharmony_ci			uinfo->count = eld->eld_size;
146462306a36Sopenharmony_ci			break;
146562306a36Sopenharmony_ci		}
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	mutex_unlock(&hdmi->pin_mutex);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	return 0;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
147462306a36Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
147762306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
147862306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
147962306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
148062306a36Sopenharmony_ci	struct hdac_hdmi_eld *eld;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data));
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
148562306a36Sopenharmony_ci	if (!pcm) {
148662306a36Sopenharmony_ci		dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
148762306a36Sopenharmony_ci			kcontrol->id.device);
148862306a36Sopenharmony_ci		return 0;
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (list_empty(&pcm->port_list)) {
149262306a36Sopenharmony_ci		dev_dbg(component->dev, "%s: empty port list, device %d\n",
149362306a36Sopenharmony_ci			__func__, kcontrol->id.device);
149462306a36Sopenharmony_ci		return 0;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	mutex_lock(&hdmi->pin_mutex);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	list_for_each_entry(port, &pcm->port_list, head) {
150062306a36Sopenharmony_ci		eld = &port->eld;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci		if (!eld->eld_valid)
150362306a36Sopenharmony_ci			continue;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci		if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
150662306a36Sopenharmony_ci		    eld->eld_size > ELD_MAX_SIZE) {
150762306a36Sopenharmony_ci			mutex_unlock(&hdmi->pin_mutex);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci			dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
151062306a36Sopenharmony_ci				__func__, kcontrol->id.device, eld->eld_size);
151162306a36Sopenharmony_ci			snd_BUG();
151262306a36Sopenharmony_ci			return -EINVAL;
151362306a36Sopenharmony_ci		}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
151662306a36Sopenharmony_ci		       eld->eld_size);
151762306a36Sopenharmony_ci		break;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	mutex_unlock(&hdmi->pin_mutex);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	return 0;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
152862306a36Sopenharmony_ci	struct snd_kcontrol_new hdmi_eld_ctl = {
152962306a36Sopenharmony_ci		.access	= SNDRV_CTL_ELEM_ACCESS_READ |
153062306a36Sopenharmony_ci			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
153162306a36Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
153262306a36Sopenharmony_ci		.name	= "ELD",
153362306a36Sopenharmony_ci		.info	= hdac_hdmi_eld_ctl_info,
153462306a36Sopenharmony_ci		.get	= hdac_hdmi_eld_ctl_get,
153562306a36Sopenharmony_ci		.device	= pcm->pcm_id,
153662306a36Sopenharmony_ci	};
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	/* add ELD ctl with the device number corresponding to the PCM stream */
153962306a36Sopenharmony_ci	kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
154062306a36Sopenharmony_ci	if (!kctl)
154162306a36Sopenharmony_ci		return -ENOMEM;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	pcm->eld_ctl = kctl;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	return snd_ctl_add(component->card->snd_card, kctl);
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops hdmi_dai_ops = {
154962306a36Sopenharmony_ci	.startup = hdac_hdmi_pcm_open,
155062306a36Sopenharmony_ci	.shutdown = hdac_hdmi_pcm_close,
155162306a36Sopenharmony_ci	.hw_params = hdac_hdmi_set_hw_params,
155262306a36Sopenharmony_ci	.set_stream = hdac_hdmi_set_stream,
155362306a36Sopenharmony_ci};
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci/*
155662306a36Sopenharmony_ci * Each converter can support a stream independently. So a dai is created
155762306a36Sopenharmony_ci * based on the number of converter queried.
155862306a36Sopenharmony_ci */
155962306a36Sopenharmony_cistatic int hdac_hdmi_create_dais(struct hdac_device *hdev,
156062306a36Sopenharmony_ci		struct snd_soc_dai_driver **dais,
156162306a36Sopenharmony_ci		struct hdac_hdmi_priv *hdmi, int num_dais)
156262306a36Sopenharmony_ci{
156362306a36Sopenharmony_ci	struct snd_soc_dai_driver *hdmi_dais;
156462306a36Sopenharmony_ci	struct hdac_hdmi_cvt *cvt;
156562306a36Sopenharmony_ci	char name[NAME_SIZE], dai_name[NAME_SIZE];
156662306a36Sopenharmony_ci	int i = 0;
156762306a36Sopenharmony_ci	u32 rates, bps;
156862306a36Sopenharmony_ci	unsigned int rate_max = 384000, rate_min = 8000;
156962306a36Sopenharmony_ci	u64 formats;
157062306a36Sopenharmony_ci	int ret;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	hdmi_dais = devm_kzalloc(&hdev->dev,
157362306a36Sopenharmony_ci			(sizeof(*hdmi_dais) * num_dais),
157462306a36Sopenharmony_ci			GFP_KERNEL);
157562306a36Sopenharmony_ci	if (!hdmi_dais)
157662306a36Sopenharmony_ci		return -ENOMEM;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	list_for_each_entry(cvt, &hdmi->cvt_list, head) {
157962306a36Sopenharmony_ci		ret = snd_hdac_query_supported_pcm(hdev, cvt->nid,
158062306a36Sopenharmony_ci					&rates,	&formats, &bps);
158162306a36Sopenharmony_ci		if (ret)
158262306a36Sopenharmony_ci			return ret;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		/* Filter out 44.1, 88.2 and 176.4Khz */
158562306a36Sopenharmony_ci		rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |
158662306a36Sopenharmony_ci			   SNDRV_PCM_RATE_176400);
158762306a36Sopenharmony_ci		if (!rates)
158862306a36Sopenharmony_ci			return -EINVAL;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci		sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
159162306a36Sopenharmony_ci		hdmi_dais[i].name = devm_kstrdup(&hdev->dev,
159262306a36Sopenharmony_ci					dai_name, GFP_KERNEL);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci		if (!hdmi_dais[i].name)
159562306a36Sopenharmony_ci			return -ENOMEM;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		snprintf(name, sizeof(name), "hifi%d", i+1);
159862306a36Sopenharmony_ci		hdmi_dais[i].playback.stream_name =
159962306a36Sopenharmony_ci				devm_kstrdup(&hdev->dev, name, GFP_KERNEL);
160062306a36Sopenharmony_ci		if (!hdmi_dais[i].playback.stream_name)
160162306a36Sopenharmony_ci			return -ENOMEM;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci		/*
160462306a36Sopenharmony_ci		 * Set caps based on capability queried from the converter.
160562306a36Sopenharmony_ci		 * It will be constrained runtime based on ELD queried.
160662306a36Sopenharmony_ci		 */
160762306a36Sopenharmony_ci		hdmi_dais[i].playback.formats = formats;
160862306a36Sopenharmony_ci		hdmi_dais[i].playback.rates = rates;
160962306a36Sopenharmony_ci		hdmi_dais[i].playback.rate_max = rate_max;
161062306a36Sopenharmony_ci		hdmi_dais[i].playback.rate_min = rate_min;
161162306a36Sopenharmony_ci		hdmi_dais[i].playback.channels_min = 2;
161262306a36Sopenharmony_ci		hdmi_dais[i].playback.channels_max = 2;
161362306a36Sopenharmony_ci		hdmi_dais[i].playback.sig_bits = bps;
161462306a36Sopenharmony_ci		hdmi_dais[i].ops = &hdmi_dai_ops;
161562306a36Sopenharmony_ci		i++;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	*dais = hdmi_dais;
161962306a36Sopenharmony_ci	hdmi->dai_drv = hdmi_dais;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	return 0;
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci/*
162562306a36Sopenharmony_ci * Parse all nodes and store the cvt/pin nids in array
162662306a36Sopenharmony_ci * Add one time initialization for pin and cvt widgets
162762306a36Sopenharmony_ci */
162862306a36Sopenharmony_cistatic int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev,
162962306a36Sopenharmony_ci		struct snd_soc_dai_driver **dais, int *num_dais)
163062306a36Sopenharmony_ci{
163162306a36Sopenharmony_ci	hda_nid_t nid;
163262306a36Sopenharmony_ci	int i, num_nodes;
163362306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
163462306a36Sopenharmony_ci	int ret;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	hdac_hdmi_skl_enable_all_pins(hdev);
163762306a36Sopenharmony_ci	hdac_hdmi_skl_enable_dp12(hdev);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid);
164062306a36Sopenharmony_ci	if (!nid || num_nodes <= 0) {
164162306a36Sopenharmony_ci		dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n");
164262306a36Sopenharmony_ci		return -EINVAL;
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++, nid++) {
164662306a36Sopenharmony_ci		unsigned int caps;
164762306a36Sopenharmony_ci		unsigned int type;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci		caps = get_wcaps(hdev, nid);
165062306a36Sopenharmony_ci		type = get_wcaps_type(caps);
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci		if (!(caps & AC_WCAP_DIGITAL))
165362306a36Sopenharmony_ci			continue;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci		switch (type) {
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci		case AC_WID_AUD_OUT:
165862306a36Sopenharmony_ci			ret = hdac_hdmi_add_cvt(hdev, nid);
165962306a36Sopenharmony_ci			if (ret < 0)
166062306a36Sopenharmony_ci				return ret;
166162306a36Sopenharmony_ci			break;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci		case AC_WID_PIN:
166462306a36Sopenharmony_ci			ret = hdac_hdmi_add_pin(hdev, nid);
166562306a36Sopenharmony_ci			if (ret < 0)
166662306a36Sopenharmony_ci				return ret;
166762306a36Sopenharmony_ci			break;
166862306a36Sopenharmony_ci		}
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	if (!hdmi->num_pin || !hdmi->num_cvt) {
167262306a36Sopenharmony_ci		ret = -EIO;
167362306a36Sopenharmony_ci		dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__);
167462306a36Sopenharmony_ci		return ret;
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt);
167862306a36Sopenharmony_ci	if (ret) {
167962306a36Sopenharmony_ci		dev_err(&hdev->dev, "Failed to create dais with err: %d\n",
168062306a36Sopenharmony_ci			ret);
168162306a36Sopenharmony_ci		return ret;
168262306a36Sopenharmony_ci	}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	*num_dais = hdmi->num_cvt;
168562306a36Sopenharmony_ci	ret = hdac_hdmi_init_dai_map(hdev);
168662306a36Sopenharmony_ci	if (ret < 0)
168762306a36Sopenharmony_ci		dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n",
168862306a36Sopenharmony_ci			ret);
168962306a36Sopenharmony_ci	return ret;
169062306a36Sopenharmony_ci}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_cistatic int hdac_hdmi_pin2port(void *aptr, int pin)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	return pin - 4; /* map NID 0x05 -> port #1 */
169562306a36Sopenharmony_ci}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_cistatic void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
169862306a36Sopenharmony_ci{
169962306a36Sopenharmony_ci	struct hdac_device *hdev = aptr;
170062306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
170162306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
170262306a36Sopenharmony_ci	struct hdac_hdmi_port *hport = NULL;
170362306a36Sopenharmony_ci	struct snd_soc_component *component = hdmi->component;
170462306a36Sopenharmony_ci	int i;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	/* Don't know how this mapping is derived */
170762306a36Sopenharmony_ci	hda_nid_t pin_nid = port + 0x04;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__,
171062306a36Sopenharmony_ci							pin_nid, pipe);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/*
171362306a36Sopenharmony_ci	 * skip notification during system suspend (but not in runtime PM);
171462306a36Sopenharmony_ci	 * the state will be updated at resume. Also since the ELD and
171562306a36Sopenharmony_ci	 * connection states are updated in anyway at the end of the resume,
171662306a36Sopenharmony_ci	 * we can skip it when received during PM process.
171762306a36Sopenharmony_ci	 */
171862306a36Sopenharmony_ci	if (snd_power_get_state(component->card->snd_card) !=
171962306a36Sopenharmony_ci			SNDRV_CTL_POWER_D0)
172062306a36Sopenharmony_ci		return;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	if (atomic_read(&hdev->in_pm))
172362306a36Sopenharmony_ci		return;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
172662306a36Sopenharmony_ci		if (pin->nid != pin_nid)
172762306a36Sopenharmony_ci			continue;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci		/* In case of non MST pin, pipe is -1 */
173062306a36Sopenharmony_ci		if (pipe == -1) {
173162306a36Sopenharmony_ci			pin->mst_capable = false;
173262306a36Sopenharmony_ci			/* if not MST, default is port[0] */
173362306a36Sopenharmony_ci			hport = &pin->ports[0];
173462306a36Sopenharmony_ci		} else {
173562306a36Sopenharmony_ci			for (i = 0; i < pin->num_ports; i++) {
173662306a36Sopenharmony_ci				pin->mst_capable = true;
173762306a36Sopenharmony_ci				if (pin->ports[i].id == pipe) {
173862306a36Sopenharmony_ci					hport = &pin->ports[i];
173962306a36Sopenharmony_ci					break;
174062306a36Sopenharmony_ci				}
174162306a36Sopenharmony_ci			}
174262306a36Sopenharmony_ci		}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci		if (hport)
174562306a36Sopenharmony_ci			hdac_hdmi_present_sense(pin, hport);
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_cistatic struct drm_audio_component_audio_ops aops = {
175162306a36Sopenharmony_ci	.pin2port	= hdac_hdmi_pin2port,
175262306a36Sopenharmony_ci	.pin_eld_notify	= hdac_hdmi_eld_notify_cb,
175362306a36Sopenharmony_ci};
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_cistatic struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
175662306a36Sopenharmony_ci						int device)
175762306a36Sopenharmony_ci{
175862306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	for_each_card_rtds(card, rtd) {
176162306a36Sopenharmony_ci		if (rtd->pcm && (rtd->pcm->device == device))
176262306a36Sopenharmony_ci			return rtd->pcm;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	return NULL;
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci/* create jack pin kcontrols */
176962306a36Sopenharmony_cistatic int create_fill_jack_kcontrols(struct snd_soc_card *card,
177062306a36Sopenharmony_ci				    struct hdac_device *hdev)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
177362306a36Sopenharmony_ci	struct snd_kcontrol_new *kc;
177462306a36Sopenharmony_ci	char kc_name[NAME_SIZE], xname[NAME_SIZE];
177562306a36Sopenharmony_ci	char *name;
177662306a36Sopenharmony_ci	int i = 0, j;
177762306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
177862306a36Sopenharmony_ci	struct snd_soc_component *component = hdmi->component;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	kc = devm_kcalloc(component->dev, hdmi->num_ports,
178162306a36Sopenharmony_ci				sizeof(*kc), GFP_KERNEL);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	if (!kc)
178462306a36Sopenharmony_ci		return -ENOMEM;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
178762306a36Sopenharmony_ci		for (j = 0; j < pin->num_ports; j++) {
178862306a36Sopenharmony_ci			snprintf(xname, sizeof(xname), "hif%d-%d Jack",
178962306a36Sopenharmony_ci						pin->nid, pin->ports[j].id);
179062306a36Sopenharmony_ci			name = devm_kstrdup(component->dev, xname, GFP_KERNEL);
179162306a36Sopenharmony_ci			if (!name)
179262306a36Sopenharmony_ci				return -ENOMEM;
179362306a36Sopenharmony_ci			snprintf(kc_name, sizeof(kc_name), "%s Switch", xname);
179462306a36Sopenharmony_ci			kc[i].name = devm_kstrdup(component->dev, kc_name,
179562306a36Sopenharmony_ci							GFP_KERNEL);
179662306a36Sopenharmony_ci			if (!kc[i].name)
179762306a36Sopenharmony_ci				return -ENOMEM;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci			kc[i].private_value = (unsigned long)name;
180062306a36Sopenharmony_ci			kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
180162306a36Sopenharmony_ci			kc[i].access = 0;
180262306a36Sopenharmony_ci			kc[i].info = snd_soc_dapm_info_pin_switch;
180362306a36Sopenharmony_ci			kc[i].put = snd_soc_dapm_put_pin_switch;
180462306a36Sopenharmony_ci			kc[i].get = snd_soc_dapm_get_pin_switch;
180562306a36Sopenharmony_ci			i++;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	return snd_soc_add_card_controls(card, kc, i);
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ciint hdac_hdmi_jack_port_init(struct snd_soc_component *component,
181362306a36Sopenharmony_ci			struct snd_soc_dapm_context *dapm)
181462306a36Sopenharmony_ci{
181562306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
181662306a36Sopenharmony_ci	struct hdac_device *hdev = hdmi->hdev;
181762306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
181862306a36Sopenharmony_ci	struct snd_soc_dapm_widget *widgets;
181962306a36Sopenharmony_ci	struct snd_soc_dapm_route *route;
182062306a36Sopenharmony_ci	char w_name[NAME_SIZE];
182162306a36Sopenharmony_ci	int i = 0, j, ret;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	widgets = devm_kcalloc(dapm->dev, hdmi->num_ports,
182462306a36Sopenharmony_ci				sizeof(*widgets), GFP_KERNEL);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	if (!widgets)
182762306a36Sopenharmony_ci		return -ENOMEM;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	route = devm_kcalloc(dapm->dev, hdmi->num_ports,
183062306a36Sopenharmony_ci				sizeof(*route), GFP_KERNEL);
183162306a36Sopenharmony_ci	if (!route)
183262306a36Sopenharmony_ci		return -ENOMEM;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	/* create Jack DAPM widget */
183562306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
183662306a36Sopenharmony_ci		for (j = 0; j < pin->num_ports; j++) {
183762306a36Sopenharmony_ci			snprintf(w_name, sizeof(w_name), "hif%d-%d Jack",
183862306a36Sopenharmony_ci						pin->nid, pin->ports[j].id);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci			ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
184162306a36Sopenharmony_ci					snd_soc_dapm_spk, NULL,
184262306a36Sopenharmony_ci					w_name, NULL, NULL, 0, NULL, 0);
184362306a36Sopenharmony_ci			if (ret < 0)
184462306a36Sopenharmony_ci				return ret;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci			pin->ports[j].jack_pin = widgets[i].name;
184762306a36Sopenharmony_ci			pin->ports[j].dapm = dapm;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci			/* add to route from Jack widget to output */
185062306a36Sopenharmony_ci			hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin,
185162306a36Sopenharmony_ci					NULL, pin->ports[j].output_pin, NULL);
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci			i++;
185462306a36Sopenharmony_ci		}
185562306a36Sopenharmony_ci	}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	/* Add Route from Jack widget to the output widget */
185862306a36Sopenharmony_ci	ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports);
185962306a36Sopenharmony_ci	if (ret < 0)
186062306a36Sopenharmony_ci		return ret;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports);
186362306a36Sopenharmony_ci	if (ret < 0)
186462306a36Sopenharmony_ci		return ret;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	ret = snd_soc_dapm_new_widgets(dapm->card);
186762306a36Sopenharmony_ci	if (ret < 0)
186862306a36Sopenharmony_ci		return ret;
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	/* Add Jack Pin switch Kcontrol */
187162306a36Sopenharmony_ci	ret = create_fill_jack_kcontrols(dapm->card, hdev);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (ret < 0)
187462306a36Sopenharmony_ci		return ret;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	/* default set the Jack Pin switch to OFF */
187762306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
187862306a36Sopenharmony_ci		for (j = 0; j < pin->num_ports; j++)
187962306a36Sopenharmony_ci			snd_soc_dapm_disable_pin(pin->ports[j].dapm,
188062306a36Sopenharmony_ci						pin->ports[j].jack_pin);
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	return 0;
188462306a36Sopenharmony_ci}
188562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ciint hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
188862306a36Sopenharmony_ci				struct snd_soc_jack *jack)
188962306a36Sopenharmony_ci{
189062306a36Sopenharmony_ci	struct snd_soc_component *component = dai->component;
189162306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
189262306a36Sopenharmony_ci	struct hdac_device *hdev = hdmi->hdev;
189362306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm;
189462306a36Sopenharmony_ci	struct snd_pcm *snd_pcm;
189562306a36Sopenharmony_ci	int err;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/*
189862306a36Sopenharmony_ci	 * this is a new PCM device, create new pcm and
189962306a36Sopenharmony_ci	 * add to the pcm list
190062306a36Sopenharmony_ci	 */
190162306a36Sopenharmony_ci	pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL);
190262306a36Sopenharmony_ci	if (!pcm)
190362306a36Sopenharmony_ci		return -ENOMEM;
190462306a36Sopenharmony_ci	pcm->pcm_id = device;
190562306a36Sopenharmony_ci	pcm->cvt = hdmi->dai_map[dai->id].cvt;
190662306a36Sopenharmony_ci	pcm->jack_event = 0;
190762306a36Sopenharmony_ci	pcm->jack = jack;
190862306a36Sopenharmony_ci	mutex_init(&pcm->lock);
190962306a36Sopenharmony_ci	INIT_LIST_HEAD(&pcm->port_list);
191062306a36Sopenharmony_ci	snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
191162306a36Sopenharmony_ci	if (snd_pcm) {
191262306a36Sopenharmony_ci		err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
191362306a36Sopenharmony_ci		if (err < 0) {
191462306a36Sopenharmony_ci			dev_err(&hdev->dev,
191562306a36Sopenharmony_ci				"chmap control add failed with err: %d for pcm: %d\n",
191662306a36Sopenharmony_ci				err, device);
191762306a36Sopenharmony_ci			return err;
191862306a36Sopenharmony_ci		}
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	/* add control for ELD Bytes */
192262306a36Sopenharmony_ci	err = hdac_hdmi_create_eld_ctl(component, pcm);
192362306a36Sopenharmony_ci	if (err < 0) {
192462306a36Sopenharmony_ci		dev_err(&hdev->dev,
192562306a36Sopenharmony_ci			"eld control add failed with err: %d for pcm: %d\n",
192662306a36Sopenharmony_ci			err, device);
192762306a36Sopenharmony_ci		return err;
192862306a36Sopenharmony_ci	}
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	list_add_tail(&pcm->head, &hdmi->pcm_list);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	return 0;
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_cistatic void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev,
193762306a36Sopenharmony_ci			struct hdac_hdmi_priv *hdmi, bool detect_pin_caps)
193862306a36Sopenharmony_ci{
193962306a36Sopenharmony_ci	int i;
194062306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head) {
194362306a36Sopenharmony_ci		if (detect_pin_caps) {
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci			if (hdac_hdmi_get_port_len(hdev, pin->nid)  == 0)
194662306a36Sopenharmony_ci				pin->mst_capable = false;
194762306a36Sopenharmony_ci			else
194862306a36Sopenharmony_ci				pin->mst_capable = true;
194962306a36Sopenharmony_ci		}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci		for (i = 0; i < pin->num_ports; i++) {
195262306a36Sopenharmony_ci			if (!pin->mst_capable && i > 0)
195362306a36Sopenharmony_ci				continue;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci			hdac_hdmi_present_sense(pin, &pin->ports[i]);
195662306a36Sopenharmony_ci		}
195762306a36Sopenharmony_ci	}
195862306a36Sopenharmony_ci}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_cistatic int hdmi_codec_probe(struct snd_soc_component *component)
196162306a36Sopenharmony_ci{
196262306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
196362306a36Sopenharmony_ci	struct hdac_device *hdev = hdmi->hdev;
196462306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm =
196562306a36Sopenharmony_ci		snd_soc_component_get_dapm(component);
196662306a36Sopenharmony_ci	struct hdac_ext_link *hlink;
196762306a36Sopenharmony_ci	int ret;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	hdmi->component = component;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	/*
197262306a36Sopenharmony_ci	 * hold the ref while we probe, also no need to drop the ref on
197362306a36Sopenharmony_ci	 * exit, we call pm_runtime_suspend() so that will do for us
197462306a36Sopenharmony_ci	 */
197562306a36Sopenharmony_ci	hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
197662306a36Sopenharmony_ci	if (!hlink) {
197762306a36Sopenharmony_ci		dev_err(&hdev->dev, "hdac link not found\n");
197862306a36Sopenharmony_ci		return -EIO;
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	snd_hdac_ext_bus_link_get(hdev->bus, hlink);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	ret = create_fill_widget_route_map(dapm);
198462306a36Sopenharmony_ci	if (ret < 0)
198562306a36Sopenharmony_ci		return ret;
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	aops.audio_ptr = hdev;
198862306a36Sopenharmony_ci	ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops);
198962306a36Sopenharmony_ci	if (ret < 0) {
199062306a36Sopenharmony_ci		dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret);
199162306a36Sopenharmony_ci		return ret;
199262306a36Sopenharmony_ci	}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	hdac_hdmi_present_sense_all_pins(hdev, hdmi, true);
199562306a36Sopenharmony_ci	/* Imp: Store the card pointer in hda_codec */
199662306a36Sopenharmony_ci	hdmi->card = dapm->card->snd_card;
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	/*
199962306a36Sopenharmony_ci	 * Setup a device_link between card device and HDMI codec device.
200062306a36Sopenharmony_ci	 * The card device is the consumer and the HDMI codec device is
200162306a36Sopenharmony_ci	 * the supplier. With this setting, we can make sure that the audio
200262306a36Sopenharmony_ci	 * domain in display power will be always turned on before operating
200362306a36Sopenharmony_ci	 * on the HDMI audio codec registers.
200462306a36Sopenharmony_ci	 * Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make
200562306a36Sopenharmony_ci	 * sure the device link is freed when the machine driver is removed.
200662306a36Sopenharmony_ci	 */
200762306a36Sopenharmony_ci	device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE |
200862306a36Sopenharmony_ci			DL_FLAG_AUTOREMOVE_CONSUMER);
200962306a36Sopenharmony_ci	/*
201062306a36Sopenharmony_ci	 * hdac_device core already sets the state to active and calls
201162306a36Sopenharmony_ci	 * get_noresume. So enable runtime and set the device to suspend.
201262306a36Sopenharmony_ci	 */
201362306a36Sopenharmony_ci	pm_runtime_enable(&hdev->dev);
201462306a36Sopenharmony_ci	pm_runtime_put(&hdev->dev);
201562306a36Sopenharmony_ci	pm_runtime_suspend(&hdev->dev);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	return 0;
201862306a36Sopenharmony_ci}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_cistatic void hdmi_codec_remove(struct snd_soc_component *component)
202162306a36Sopenharmony_ci{
202262306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
202362306a36Sopenharmony_ci	struct hdac_device *hdev = hdmi->hdev;
202462306a36Sopenharmony_ci	int ret;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL);
202762306a36Sopenharmony_ci	if (ret < 0)
202862306a36Sopenharmony_ci		dev_err(&hdev->dev, "notifier unregister failed: err: %d\n",
202962306a36Sopenharmony_ci				ret);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	pm_runtime_disable(&hdev->dev);
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
203562306a36Sopenharmony_cistatic int hdmi_codec_resume(struct device *dev)
203662306a36Sopenharmony_ci{
203762306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(dev);
203862306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
203962306a36Sopenharmony_ci	int ret;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	ret = pm_runtime_force_resume(dev);
204262306a36Sopenharmony_ci	if (ret < 0)
204362306a36Sopenharmony_ci		return ret;
204462306a36Sopenharmony_ci	/*
204562306a36Sopenharmony_ci	 * As the ELD notify callback request is not entertained while the
204662306a36Sopenharmony_ci	 * device is in suspend state. Need to manually check detection of
204762306a36Sopenharmony_ci	 * all pins here. pin capablity change is not support, so use the
204862306a36Sopenharmony_ci	 * already set pin caps.
204962306a36Sopenharmony_ci	 *
205062306a36Sopenharmony_ci	 * NOTE: this is safe to call even if the codec doesn't actually resume.
205162306a36Sopenharmony_ci	 * The pin check involves only with DRM audio component hooks, so it
205262306a36Sopenharmony_ci	 * works even if the HD-audio side is still dreaming peacefully.
205362306a36Sopenharmony_ci	 */
205462306a36Sopenharmony_ci	hdac_hdmi_present_sense_all_pins(hdev, hdmi, false);
205562306a36Sopenharmony_ci	return 0;
205662306a36Sopenharmony_ci}
205762306a36Sopenharmony_ci#else
205862306a36Sopenharmony_ci#define hdmi_codec_resume NULL
205962306a36Sopenharmony_ci#endif
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_cistatic const struct snd_soc_component_driver hdmi_hda_codec = {
206262306a36Sopenharmony_ci	.probe			= hdmi_codec_probe,
206362306a36Sopenharmony_ci	.remove			= hdmi_codec_remove,
206462306a36Sopenharmony_ci	.use_pmdown_time	= 1,
206562306a36Sopenharmony_ci	.endianness		= 1,
206662306a36Sopenharmony_ci};
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_cistatic void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx,
206962306a36Sopenharmony_ci					unsigned char *chmap)
207062306a36Sopenharmony_ci{
207162306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
207262306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap));
207562306a36Sopenharmony_ci}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_cistatic void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx,
207862306a36Sopenharmony_ci				unsigned char *chmap, int prepared)
207962306a36Sopenharmony_ci{
208062306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
208162306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
208262306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	if (!pcm)
208562306a36Sopenharmony_ci		return;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	if (list_empty(&pcm->port_list))
208862306a36Sopenharmony_ci		return;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	mutex_lock(&pcm->lock);
209162306a36Sopenharmony_ci	pcm->chmap_set = true;
209262306a36Sopenharmony_ci	memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap));
209362306a36Sopenharmony_ci	list_for_each_entry(port, &pcm->port_list, head)
209462306a36Sopenharmony_ci		if (prepared)
209562306a36Sopenharmony_ci			hdac_hdmi_setup_audio_infoframe(hdev, pcm, port);
209662306a36Sopenharmony_ci	mutex_unlock(&pcm->lock);
209762306a36Sopenharmony_ci}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_cistatic bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx)
210062306a36Sopenharmony_ci{
210162306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
210262306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	if (!pcm)
210562306a36Sopenharmony_ci		return false;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	if (list_empty(&pcm->port_list))
210862306a36Sopenharmony_ci		return false;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	return true;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cistatic int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
211662306a36Sopenharmony_ci	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
211762306a36Sopenharmony_ci	struct hdac_hdmi_port *port;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	if (!pcm)
212062306a36Sopenharmony_ci		return 0;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	if (list_empty(&pcm->port_list))
212362306a36Sopenharmony_ci		return 0;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head);
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	if (!port || !port->eld.eld_valid)
212862306a36Sopenharmony_ci		return 0;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	return port->eld.info.spk_alloc;
213162306a36Sopenharmony_ci}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_cistatic struct hdac_hdmi_drv_data intel_glk_drv_data  = {
213462306a36Sopenharmony_ci	.vendor_nid = INTEL_GLK_VENDOR_NID,
213562306a36Sopenharmony_ci};
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_cistatic struct hdac_hdmi_drv_data intel_drv_data  = {
213862306a36Sopenharmony_ci	.vendor_nid = INTEL_VENDOR_NID,
213962306a36Sopenharmony_ci};
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_cistatic int hdac_hdmi_dev_probe(struct hdac_device *hdev)
214262306a36Sopenharmony_ci{
214362306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi_priv;
214462306a36Sopenharmony_ci	struct snd_soc_dai_driver *hdmi_dais = NULL;
214562306a36Sopenharmony_ci	struct hdac_ext_link *hlink;
214662306a36Sopenharmony_ci	int num_dais = 0;
214762306a36Sopenharmony_ci	int ret;
214862306a36Sopenharmony_ci	struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver);
214962306a36Sopenharmony_ci	const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	/* hold the ref while we probe */
215262306a36Sopenharmony_ci	hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
215362306a36Sopenharmony_ci	if (!hlink) {
215462306a36Sopenharmony_ci		dev_err(&hdev->dev, "hdac link not found\n");
215562306a36Sopenharmony_ci		return -EIO;
215662306a36Sopenharmony_ci	}
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	snd_hdac_ext_bus_link_get(hdev->bus, hlink);
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL);
216162306a36Sopenharmony_ci	if (hdmi_priv == NULL)
216262306a36Sopenharmony_ci		return -ENOMEM;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap);
216562306a36Sopenharmony_ci	hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
216662306a36Sopenharmony_ci	hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
216762306a36Sopenharmony_ci	hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
216862306a36Sopenharmony_ci	hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
216962306a36Sopenharmony_ci	hdmi_priv->hdev = hdev;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	if (!hdac_id)
217262306a36Sopenharmony_ci		return -ENODEV;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	if (hdac_id->driver_data)
217562306a36Sopenharmony_ci		hdmi_priv->drv_data =
217662306a36Sopenharmony_ci			(struct hdac_hdmi_drv_data *)hdac_id->driver_data;
217762306a36Sopenharmony_ci	else
217862306a36Sopenharmony_ci		hdmi_priv->drv_data = &intel_drv_data;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	dev_set_drvdata(&hdev->dev, hdmi_priv);
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	INIT_LIST_HEAD(&hdmi_priv->pin_list);
218362306a36Sopenharmony_ci	INIT_LIST_HEAD(&hdmi_priv->cvt_list);
218462306a36Sopenharmony_ci	INIT_LIST_HEAD(&hdmi_priv->pcm_list);
218562306a36Sopenharmony_ci	mutex_init(&hdmi_priv->pin_mutex);
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	/*
218862306a36Sopenharmony_ci	 * Turned off in the runtime_suspend during the first explicit
218962306a36Sopenharmony_ci	 * pm_runtime_suspend call.
219062306a36Sopenharmony_ci	 */
219162306a36Sopenharmony_ci	snd_hdac_display_power(hdev->bus, hdev->addr, true);
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais);
219462306a36Sopenharmony_ci	if (ret < 0) {
219562306a36Sopenharmony_ci		dev_err(&hdev->dev,
219662306a36Sopenharmony_ci			"Failed in parse and map nid with err: %d\n", ret);
219762306a36Sopenharmony_ci		return ret;
219862306a36Sopenharmony_ci	}
219962306a36Sopenharmony_ci	snd_hdac_refresh_widgets(hdev);
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	/* ASoC specific initialization */
220262306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec,
220362306a36Sopenharmony_ci					hdmi_dais, num_dais);
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	snd_hdac_ext_bus_link_put(hdev->bus, hlink);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	return ret;
220862306a36Sopenharmony_ci}
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_cistatic void clear_dapm_works(struct hdac_device *hdev)
221162306a36Sopenharmony_ci{
221262306a36Sopenharmony_ci	struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
221362306a36Sopenharmony_ci	struct hdac_hdmi_pin *pin;
221462306a36Sopenharmony_ci	int i;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	list_for_each_entry(pin, &hdmi->pin_list, head)
221762306a36Sopenharmony_ci		for (i = 0; i < pin->num_ports; i++)
221862306a36Sopenharmony_ci			cancel_work_sync(&pin->ports[i].dapm_work);
221962306a36Sopenharmony_ci}
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_cistatic int hdac_hdmi_dev_remove(struct hdac_device *hdev)
222262306a36Sopenharmony_ci{
222362306a36Sopenharmony_ci	clear_dapm_works(hdev);
222462306a36Sopenharmony_ci	snd_hdac_display_power(hdev->bus, hdev->addr, false);
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	return 0;
222762306a36Sopenharmony_ci}
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci#ifdef CONFIG_PM
223062306a36Sopenharmony_cistatic int hdac_hdmi_runtime_suspend(struct device *dev)
223162306a36Sopenharmony_ci{
223262306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(dev);
223362306a36Sopenharmony_ci	struct hdac_bus *bus = hdev->bus;
223462306a36Sopenharmony_ci	struct hdac_ext_link *hlink;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	dev_dbg(dev, "Enter: %s\n", __func__);
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	/* controller may not have been initialized for the first time */
223962306a36Sopenharmony_ci	if (!bus)
224062306a36Sopenharmony_ci		return 0;
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	/*
224362306a36Sopenharmony_ci	 * Power down afg.
224462306a36Sopenharmony_ci	 * codec_read is preferred over codec_write to set the power state.
224562306a36Sopenharmony_ci	 * This way verb is send to set the power state and response
224662306a36Sopenharmony_ci	 * is received. So setting power state is ensured without using loop
224762306a36Sopenharmony_ci	 * to read the state.
224862306a36Sopenharmony_ci	 */
224962306a36Sopenharmony_ci	snd_hdac_codec_read(hdev, hdev->afg, 0,	AC_VERB_SET_POWER_STATE,
225062306a36Sopenharmony_ci							AC_PWRST_D3);
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev));
225362306a36Sopenharmony_ci	if (!hlink) {
225462306a36Sopenharmony_ci		dev_err(dev, "hdac link not found\n");
225562306a36Sopenharmony_ci		return -EIO;
225662306a36Sopenharmony_ci	}
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	snd_hdac_codec_link_down(hdev);
225962306a36Sopenharmony_ci	snd_hdac_ext_bus_link_put(bus, hlink);
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	snd_hdac_display_power(bus, hdev->addr, false);
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	return 0;
226462306a36Sopenharmony_ci}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_cistatic int hdac_hdmi_runtime_resume(struct device *dev)
226762306a36Sopenharmony_ci{
226862306a36Sopenharmony_ci	struct hdac_device *hdev = dev_to_hdac_dev(dev);
226962306a36Sopenharmony_ci	struct hdac_bus *bus = hdev->bus;
227062306a36Sopenharmony_ci	struct hdac_ext_link *hlink;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	dev_dbg(dev, "Enter: %s\n", __func__);
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	/* controller may not have been initialized for the first time */
227562306a36Sopenharmony_ci	if (!bus)
227662306a36Sopenharmony_ci		return 0;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev));
227962306a36Sopenharmony_ci	if (!hlink) {
228062306a36Sopenharmony_ci		dev_err(dev, "hdac link not found\n");
228162306a36Sopenharmony_ci		return -EIO;
228262306a36Sopenharmony_ci	}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	snd_hdac_ext_bus_link_get(bus, hlink);
228562306a36Sopenharmony_ci	snd_hdac_codec_link_up(hdev);
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	snd_hdac_display_power(bus, hdev->addr, true);
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	hdac_hdmi_skl_enable_all_pins(hdev);
229062306a36Sopenharmony_ci	hdac_hdmi_skl_enable_dp12(hdev);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	/* Power up afg */
229362306a36Sopenharmony_ci	snd_hdac_codec_read(hdev, hdev->afg, 0,	AC_VERB_SET_POWER_STATE,
229462306a36Sopenharmony_ci							AC_PWRST_D0);
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	return 0;
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ci#else
229962306a36Sopenharmony_ci#define hdac_hdmi_runtime_suspend NULL
230062306a36Sopenharmony_ci#define hdac_hdmi_runtime_resume NULL
230162306a36Sopenharmony_ci#endif
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_cistatic const struct dev_pm_ops hdac_hdmi_pm = {
230462306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
230562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume)
230662306a36Sopenharmony_ci};
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_cistatic const struct hda_device_id hdmi_list[] = {
230962306a36Sopenharmony_ci	HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
231062306a36Sopenharmony_ci	HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
231162306a36Sopenharmony_ci	HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
231262306a36Sopenharmony_ci	HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI",
231362306a36Sopenharmony_ci						   &intel_glk_drv_data),
231462306a36Sopenharmony_ci	HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI",
231562306a36Sopenharmony_ci						   &intel_glk_drv_data),
231662306a36Sopenharmony_ci	{}
231762306a36Sopenharmony_ci};
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, hdmi_list);
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_cistatic struct hdac_driver hdmi_driver = {
232262306a36Sopenharmony_ci	.driver = {
232362306a36Sopenharmony_ci		.name   = "HDMI HDA Codec",
232462306a36Sopenharmony_ci		.pm = &hdac_hdmi_pm,
232562306a36Sopenharmony_ci	},
232662306a36Sopenharmony_ci	.id_table       = hdmi_list,
232762306a36Sopenharmony_ci	.probe          = hdac_hdmi_dev_probe,
232862306a36Sopenharmony_ci	.remove         = hdac_hdmi_dev_remove,
232962306a36Sopenharmony_ci};
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_cistatic int __init hdmi_init(void)
233262306a36Sopenharmony_ci{
233362306a36Sopenharmony_ci	return snd_hda_ext_driver_register(&hdmi_driver);
233462306a36Sopenharmony_ci}
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_cistatic void __exit hdmi_exit(void)
233762306a36Sopenharmony_ci{
233862306a36Sopenharmony_ci	snd_hda_ext_driver_unregister(&hdmi_driver);
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_cimodule_init(hdmi_init);
234262306a36Sopenharmony_cimodule_exit(hdmi_exit);
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
234562306a36Sopenharmony_ciMODULE_DESCRIPTION("HDMI HD codec");
234662306a36Sopenharmony_ciMODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
234762306a36Sopenharmony_ciMODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
2348