--- old/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c	Sun Mar 31 19:53:28 2019
+++ new/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c	Sun Mar 31 19:53:28 2019
@@ -26,6 +26,8 @@
 #include <sys/note.h>
 #include <sys/beep.h>
 #include <sys/pci.h>
+#include <sys/stddef.h>
+#include <sys/sysmacros.h>
 #include "audiohd.h"
 
 #define	DRVNAME			"audiohd"
@@ -84,6 +86,9 @@
     audiohd_path_t *, uint64_t);
 static void audiohd_set_beep_volume(audiohd_state_t *);
 static int audiohd_set_beep(void *, uint64_t);
+static void audiohd_setup_hdmi_dp(audiohd_state_t *, uint8_t, uint8_t,
+    boolean_t);
+static audiohd_eld_t *audiohd_get_eld(audiohd_state_t *, uint8_t, uint8_t);
 static void audiohd_pin_sense(audiohd_state_t *, uint32_t, uint32_t);
 
 static	int	audiohd_beep;
@@ -333,7 +338,7 @@
 		goto error;
 	}
 
-	/* Set PCI command register to enable bus master and memeory I/O */
+	/* Set PCI command register to enable bus master and memory I/O */
 	if (audiohd_init_pci(statep, &hda_dev_accattr) != DDI_SUCCESS) {
 		audio_dev_warn(statep->adev,
 		    "couldn't init pci regs");
@@ -582,7 +587,8 @@
 		wid = path->pin_wid[i];
 		widget = codec->widget[wid];
 		pin = (audiohd_pin_t *)widget->priv;
-		if (pin->device == DTYPE_SPDIF_OUT) {
+		if (pin->device == DTYPE_SPDIF_OUT ||
+		    pin->device == DTYPE_DIGIT_OUT) {
 			ctrl = audioha_codec_verb_get(
 			    statep,
 			    codec->index,
@@ -666,7 +672,17 @@
 		    statep->port[PORT_DAC]->format << 4 |
 		    statep->pchan - 1);
 	}
+
+	/* Setup HDMI/DP output pin */
+	if (pin->device == DTYPE_DIGIT_OUT) {
+		pin->eld = audiohd_get_eld(statep, codec->index, pin->wid);
+		if (pin->eld != NULL &&
+		    (pin->eld->eld_conn_type & 0x3) != 0)
+			audiohd_setup_hdmi_dp(statep, codec->index, pin->wid,
+			    pin->eld->eld_conn_type == AUDIOHD_ELD_CONN_HDMI);
+	}
 }
+
 static void
 audiohd_init_record_path(audiohd_path_t *path)
 {
@@ -2142,7 +2158,7 @@
 	case AUDIOHD_VID_INTEL:
 		/*
 		 * Currently, Intel (G)MCH and ICHx chipsets support PCI
-		 * Express QoS. It implemenets two VCs(virtual channels)
+		 * Express QoS. It implements two VCs (virtual channels)
 		 * and allows OS software to map 8 traffic classes to the
 		 * two VCs. Some BIOSes initialize HD audio hardware to
 		 * use TC7 (traffic class 7) and to map TC7 to VC1 as Intel
@@ -2724,6 +2740,7 @@
 	if ((widget->widget_cap & AUDIOHD_URCAP_MASK) &&
 	    (pin->cap & AUDIOHD_DTCCAP_MASK) &&
 	    ((pin->device == DTYPE_LINEOUT) ||
+	    (pin->device == DTYPE_DIGIT_OUT) ||
 	    (pin->device == DTYPE_SPDIF_OUT) ||
 	    (pin->device == DTYPE_HP_OUT) ||
 	    (pin->device == DTYPE_MIC_IN))) {
@@ -3240,6 +3257,7 @@
 			continue;
 		if ((pin->device != DTYPE_LINEOUT) &&
 		    (pin->device != DTYPE_SPEAKER) &&
+		    (pin->device != DTYPE_DIGIT_OUT) &&
 		    (pin->device != DTYPE_SPDIF_OUT) &&
 		    (pin->device != DTYPE_HP_OUT))
 			continue;
@@ -3754,7 +3772,7 @@
 			path = kmem_zalloc(sizeof (audiohd_path_t),
 			    KM_SLEEP);
 		else
-			bzero(path, sizeof (audiohd_port_t));
+			bzero(path, sizeof (audiohd_path_t));
 
 		path->adda_wid = wid;
 
@@ -4553,6 +4571,7 @@
 			continue;
 		if ((pin->device != DTYPE_LINEOUT) &&
 		    (pin->device != DTYPE_SPEAKER) &&
+		    (pin->device != DTYPE_DIGIT_OUT) &&
 		    (pin->device != DTYPE_SPDIF_OUT) &&
 		    (pin->device != DTYPE_HP_OUT))
 			continue;
@@ -4825,6 +4844,7 @@
 		case DTYPE_LINEOUT:
 		case DTYPE_HP_OUT:
 		case DTYPE_SPDIF_OUT:
+		case DTYPE_DIGIT_OUT:
 			widget->path_flags |= AUDIOHD_PATH_LOOPBACK;
 			widget->in_weight++;
 			pin->adc_wid = path->adda_wid;
@@ -4893,7 +4913,7 @@
 		if (path == NULL)
 			path = kmem_zalloc(sizeof (audiohd_path_t), KM_SLEEP);
 		else
-			bzero(path, sizeof (audiohd_port_t));
+			bzero(path, sizeof (audiohd_path_t));
 		path->adda_wid = wid;
 
 		for (i = 0; i < widget->nconns; i++) {
@@ -5287,6 +5307,7 @@
 			    (AUDIOHD_URCAP_MASK) &&
 			    (pin->cap & AUDIOHD_DTCCAP_MASK)) &&
 			    ((pin->device == DTYPE_LINEOUT) ||
+			    (pin->device == DTYPE_DIGIT_OUT) ||
 			    (pin->device == DTYPE_SPDIF_OUT) ||
 			    (pin->device == DTYPE_HP_OUT) ||
 			    (pin->device == DTYPE_MIC_IN))) {
@@ -5619,7 +5640,131 @@
 		}
 	}
 }
+
 /*
+ * audiohd_setup_hdmi_dp()
+ *
+ * Description
+ *
+ *	Set up a basic channel mapping and a audio infoframe for a
+ *	HDMI/DisplayPort pin widget. Due to lack of testing hardware
+ *	this is limited to 2 channels for now.
+ */
+static void
+audiohd_setup_hdmi_dp(audiohd_state_t *statep, uint8_t index, uint8_t id,
+    boolean_t hdmi)
+{
+	uint8_t chanmap[] = { 0, 1, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf };
+	uint8_t dp_infoframe[] = { 0x84, 0x1b, 0x44, 0x01, 0x00, 0x00, 0x02 };
+	uint8_t hdmi_infoframe[] = { 0x84, 0x01, 0x0a,
+	    (-(0x84 + 0x01 + 0x0a + 0x01 + 0x02)) & 0xff,
+	    0x01, 0x00, 0x00, 0x02 };
+	uint8_t *infoframe;
+	size_t infolen;
+	int i;
+
+	if (hdmi) {
+		infoframe = hdmi_infoframe;
+		infolen = sizeof (hdmi_infoframe);
+	} else {
+		infoframe = dp_infoframe;
+		infolen = sizeof (dp_infoframe);
+	}
+
+	/*
+	 * Setup channel mapping. Only 2 channels supported for now.
+	 */
+	for (i = 0; i != sizeof (chanmap); i++)
+		(void) audioha_codec_verb_get(statep, index, id,
+		    AUDIOHDC_VERB_SET_ASP_CHMAP, (chanmap[i] << 4) | i);
+
+	/*
+	 * Send audio infoframe.
+	 */
+	(void) audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+	(void) audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_SET_DIP_XMIT, AUDIOHD_DIP_XMIT_STOP);
+
+	(void) audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+	for (i = 0; i != AUDIOHD_DIP_DATA_LEN; i++)
+		(void) audioha_codec_verb_get(statep, index, id,
+		    AUDIOHDC_VERB_SET_DIP_DATA, 0);
+
+	(void) audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+	for (i = 0; i != infolen; i++)
+		(void) audioha_codec_verb_get(statep, index, id,
+		    AUDIOHDC_VERB_SET_DIP_DATA, infoframe[i]);
+
+	(void) audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+	(void) audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_SET_DIP_XMIT, AUDIOHD_DIP_XMIT_BEST_EFFORT);
+
+}
+
+/*
+ * audiohd_get_eld()
+ *
+ * Description
+ *
+ *	Fetch the EDID-like data (ELD) from a pin that is a HDMI/DP
+ *	display sink device. The data is coming tightly packed from
+ *	hardware, unpack it into a audiohd_eld_t structure for easy
+ *	access. We get the complete ELD, but we completely ignore the
+ *	vendor specific part.
+ */
+static audiohd_eld_t *
+audiohd_get_eld(audiohd_state_t *statep, uint8_t index, uint8_t id)
+{
+	uint8_t			*buf;
+	audiohd_eld_t		*eld;
+	off_t			sad_off;
+	uint32_t		rs;
+	size_t			eldsz;
+	size_t			i;
+
+	rs = audioha_codec_verb_get(statep, index, id,
+	    AUDIOHDC_VERB_GET_DIP_SIZE, AUDIOHD_DIP_SIZE_ELD);
+
+	eldsz = (rs & AUDIOHD_DIP_SIZE_ELD_MASK) + 1;
+	if (eldsz < AUDIOHD_ELD_HDR_LEN)
+		return (NULL);
+
+	eldsz = MAX(eldsz, sizeof (audiohd_eld_t));
+
+	buf = kmem_zalloc(eldsz, KM_SLEEP);
+	eld = (audiohd_eld_t *)buf;
+
+	/*
+	 * The device will return 0 for any offset that is beyond the length of
+	 * ELD, so we don't have to check whether the size returned by DIP SIZE
+	 * is less than sizeof (audiohd_eld_t).
+	 */
+	for (i = 0; i != eldsz; i++)
+		buf[i] = audioha_codec_verb_get(statep, index, id,
+		    AUDIOHDC_VERB_GET_ELD, i);
+
+	if (eld->eld_ver != AUDIOHD_ELD_VER_2 &&
+	    eld->eld_ver != AUDIOHD_ELD_VER_NODRV) {
+		kmem_free(buf, eldsz);
+		return (NULL);
+	}
+
+	eld->eld_size = eldsz;
+
+	/* move the CEA SADs to eld_cea_sad */
+	sad_off = offsetof(audiohd_eld_t, eld_monitor_name) + eld->eld_mnl;
+	bcopy(buf + sad_off, (void *)eld->eld_cea_sad,
+	    sizeof (audiohd_sad_t) * eld->eld_sad_count);
+	bzero(buf + sad_off, offsetof(audiohd_eld_t, eld_cea_sad) - sad_off);
+
+	return (eld);
+}
+
+/*
  * audiohd_pin_sense()
  *
  * Description
@@ -5627,15 +5772,18 @@
  *	When the earphone is plugged into the jack associtated with the pin
  *	complex, we disable the built in speaker. When the earphone is plugged
  *	out of the jack, we enable the built in speaker.
+ *
+ *	When a digital pin complex reports valid EDID-like data (ELD), update
+ *	it.
  */
 static void
 audiohd_pin_sense(audiohd_state_t *statep, uint32_t resp, uint32_t respex)
 {
+	audiohd_pin_t		*pin = NULL;
 	uint8_t			index;
 	uint8_t			id;
 	uint32_t		rs;
 	audiohd_widget_t	*widget;
-	audiohd_pin_t		*pin;
 	hda_codec_t		*codec;
 
 	index = respex & AUDIOHD_RIRB_CODEC_MASK;
@@ -5648,13 +5796,23 @@
 	if (widget == NULL)
 		return;
 
+	if (widget->type == WTYPE_PIN)
+		pin = (audiohd_pin_t *)widget->priv;
+
 	rs = audioha_codec_verb_get(statep, index, id,
 	    AUDIOHDC_VERB_GET_PIN_SENSE, 0);
-	if (rs & AUDIOHD_PIN_PRES_MASK) {
+
+	if ((widget->widget_cap & AUDIOHD_WIDCAP_DIGIT) != 0 &&
+	    (rs & AUDIOHD_PIN_ELDV_MASK) != 0) {
+		/* updated EDID-like data */
+		if (pin != NULL) {
+			if (pin->eld != NULL)
+				kmem_free(pin->eld, pin->eld->eld_size);
+			pin->eld = audiohd_get_eld(statep, index, id);
+		}
+	} else if (rs & AUDIOHD_PIN_PRES_MASK) {
 		/* A MIC is plugged in, we select the MIC as input */
-		if ((widget->type == WTYPE_PIN) &&
-		    (pin = (audiohd_pin_t *)widget->priv) &&
-		    (pin->device == DTYPE_MIC_IN)) {
+		if (pin != NULL && pin->device == DTYPE_MIC_IN) {
 			audiohd_select_mic(statep, index, id, 1);
 			return;
 		}
