Module Name:    src
Committed By:   snj
Date:           Tue Nov 18 18:19:10 UTC 2014

Modified Files:
        src/sys/arch/arm/allwinner [netbsd-7]: awin_hdmi.c awin_hdmiaudio.c
            awin_var.h
        src/sys/arch/evbarm/awin [netbsd-7]: awin_machdep.c
        src/sys/dev/i2c [netbsd-7]: ddc.c ddcvar.h
        src/sys/dev/videomode [netbsd-7]: edid.c edidreg.h edidvar.h

Log Message:
Pull up following revision(s) (requested by jmcneill in ticket #244):
        sys/arch/arm/allwinner/awin_hdmi.c: revision 1.13-1.15
        sys/arch/arm/allwinner/awin_hdmiaudio.c: revision 1.4
        sys/arch/arm/allwinner/awin_var.h: revision 1.25
        sys/arch/evbarm/awin/awin_machdep.c: revision 1.29
        sys/dev/i2c/ddc.c: revision 1.4
        sys/dev/i2c/ddcvar.h: revision 1.2
        sys/dev/videomode/edid.c: revision 1.13
        sys/dev/videomode/edidreg.h: revision 1.4
        sys/dev/videomode/edidvar.h: revision 1.3
Parse the extension block count field, and make it available in struct edid_info
--
add ddc_read_edid_block, which is the same as ddc_read_edid but takes an 
additional block number argument
--
Add support for DVI displays. Detect HDMI vs DVI mode by looking for a
CEA-861-D extension block in the EDID, and then searching this block for
an HDMI vendor-specific data block (HDMI VSDB).
--
Allow for overriding DVI/HDMI detection with a kernel boot arg. Set
hdmi.forcemode=dvi or hdmi.forcemode=hdmi to disable auto-detection.
--
report hotplug status for AUDIO_GETDEV fields, only allow playback if a capable 
display is connected
--
clear repeater sel bits before setting -- would cause an issue if we ever 
switched away from a pixel doubling mode


To generate a diff of this commit:
cvs rdiff -u -r1.4.2.4 -r1.4.2.5 src/sys/arch/arm/allwinner/awin_hdmi.c
cvs rdiff -u -r1.3.2.2 -r1.3.2.3 src/sys/arch/arm/allwinner/awin_hdmiaudio.c
cvs rdiff -u -r1.10.2.4 -r1.10.2.5 src/sys/arch/arm/allwinner/awin_var.h
cvs rdiff -u -r1.8.2.7 -r1.8.2.8 src/sys/arch/evbarm/awin/awin_machdep.c
cvs rdiff -u -r1.3 -r1.3.62.1 src/sys/dev/i2c/ddc.c
cvs rdiff -u -r1.1 -r1.1.138.1 src/sys/dev/i2c/ddcvar.h
cvs rdiff -u -r1.12 -r1.12.12.1 src/sys/dev/videomode/edid.c
cvs rdiff -u -r1.3 -r1.3.30.1 src/sys/dev/videomode/edidreg.h
cvs rdiff -u -r1.2 -r1.2.138.1 src/sys/dev/videomode/edidvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/allwinner/awin_hdmi.c
diff -u src/sys/arch/arm/allwinner/awin_hdmi.c:1.4.2.4 src/sys/arch/arm/allwinner/awin_hdmi.c:1.4.2.5
--- src/sys/arch/arm/allwinner/awin_hdmi.c:1.4.2.4	Fri Nov 14 13:37:39 2014
+++ src/sys/arch/arm/allwinner/awin_hdmi.c	Tue Nov 18 18:19:09 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_hdmi.c,v 1.4.2.4 2014/11/14 13:37:39 martin Exp $ */
+/* $NetBSD: awin_hdmi.c,v 1.4.2.5 2014/11/18 18:19:09 snj Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -32,7 +32,7 @@
 #define AWIN_HDMI_PLL	3	/* PLL7 or PLL3 */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.4.2.4 2014/11/14 13:37:39 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.4.2.5 2014/11/18 18:19:09 snj Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -64,6 +64,14 @@ struct awin_hdmi_softc {
 	kmutex_t sc_ic_lock;
 
 	bool sc_connected;
+	char sc_display_vendor[16];
+	char sc_display_product[16];
+
+	u_int sc_display_mode;
+	u_int sc_current_display_mode;
+#define DISPLAY_MODE_AUTO	0
+#define DISPLAY_MODE_HDMI	1
+#define DISPLAY_MODE_DVI	2
 
 	uint32_t sc_ver;
 	unsigned int sc_i2c_blklen;
@@ -85,17 +93,19 @@ static int	awin_hdmi_i2c_acquire_bus(voi
 static void	awin_hdmi_i2c_release_bus(void *, int);
 static int	awin_hdmi_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
 				   size_t, void *, size_t, int);
-static int	awin_hdmi_i2c_xfer(void *, i2c_addr_t, uint8_t,
+static int	awin_hdmi_i2c_xfer(void *, i2c_addr_t, uint8_t, uint8_t,
 				   size_t, int, int);
 static int	awin_hdmi_i2c_reset(struct awin_hdmi_softc *, int);
 
 static void	awin_hdmi_enable(struct awin_hdmi_softc *);
 static void	awin_hdmi_read_edid(struct awin_hdmi_softc *);
+static u_int	awin_hdmi_get_display_mode(struct awin_hdmi_softc *,
+					   const struct edid_info *);
 static void	awin_hdmi_video_enable(struct awin_hdmi_softc *, bool);
 static void	awin_hdmi_set_videomode(struct awin_hdmi_softc *,
-					const struct videomode *);
+					const struct videomode *, u_int);
 static void	awin_hdmi_set_audiomode(struct awin_hdmi_softc *,
-					const struct videomode *);
+					const struct videomode *, u_int);
 static void	awin_hdmi_hpd(struct awin_hdmi_softc *);
 static void	awin_hdmi_thread(void *);
 #if 0
@@ -127,6 +137,7 @@ awin_hdmi_attach(device_t parent, device
 	struct awin_hdmi_softc *sc = device_private(self);
 	struct awinio_attach_args * const aio = aux;
 	const struct awin_locators * const loc = &aio->aio_loc;
+	prop_dictionary_t cfg = device_properties(self);
 	uint32_t ver, clk;
 
 	sc->sc_dev = self;
@@ -172,6 +183,15 @@ awin_hdmi_attach(device_t parent, device
 	sc->sc_ver = ver;
 	sc->sc_i2c_blklen = 16;
 
+	const char *display_mode = NULL;
+	prop_dictionary_get_cstring_nocopy(cfg, "display-mode", &display_mode);
+	if (display_mode) {
+		if (strcasecmp(display_mode, "hdmi") == 0)
+			sc->sc_display_mode = DISPLAY_MODE_HDMI;
+		else if (strcasecmp(display_mode, "dvi") == 0)
+			sc->sc_display_mode = DISPLAY_MODE_DVI;
+	}
+
 #if 0
 	sc->sc_ih = intr_establish(loc->loc_intr, IPL_SCHED, IST_LEVEL,
 	    awin_hdmi_intr, sc);
@@ -237,6 +257,7 @@ awin_hdmi_i2c_exec(void *priv, i2c_op_t 
 {
 	struct awin_hdmi_softc *sc = priv;
 	uint8_t *pbuf;
+	uint8_t block;
 	int resid;
 	off_t off;
 	int err;
@@ -251,14 +272,15 @@ awin_hdmi_i2c_exec(void *priv, i2c_op_t 
 	if (err)
 		goto done;
 
-	off = *(const uint8_t *)cmdbuf;
+	block = *(const uint8_t *)cmdbuf;
+	off = (block & 1) ? 128 : 0;
 
 	pbuf = buf;
 	resid = len;
 	while (resid > 0) {
 		size_t blklen = min(resid, sc->sc_i2c_blklen);
 
-		err = awin_hdmi_i2c_xfer(sc, addr, off, blklen,
+		err = awin_hdmi_i2c_xfer(sc, addr, block >> 1, off, blklen,
 		      AWIN_HDMI_DDC_COMMAND_ACCESS_CMD_EOREAD, flags);
 		if (err)
 			goto done;
@@ -288,7 +310,7 @@ done:
 }
 
 static int
-awin_hdmi_i2c_xfer_1_3(void *priv, i2c_addr_t addr, uint8_t reg,
+awin_hdmi_i2c_xfer_1_3(void *priv, i2c_addr_t addr, uint8_t block, uint8_t reg,
     size_t len, int type, int flags)
 {
 	struct awin_hdmi_softc *sc = priv;
@@ -299,7 +321,8 @@ awin_hdmi_i2c_xfer_1_3(void *priv, i2c_a
 	val &= ~AWIN_HDMI_DDC_CTRL_FIFO_DIR;
 	HDMI_WRITE(sc, AWIN_HDMI_DDC_CTRL_REG, val);
 
-	val = __SHIFTIN(0x60, AWIN_HDMI_DDC_SLAVE_ADDR_1);
+	val |= __SHIFTIN(block, AWIN_HDMI_DDC_SLAVE_ADDR_0);
+	val |= __SHIFTIN(0x60, AWIN_HDMI_DDC_SLAVE_ADDR_1);
 	val |= __SHIFTIN(reg, AWIN_HDMI_DDC_SLAVE_ADDR_2);
 	val |= __SHIFTIN(addr, AWIN_HDMI_DDC_SLAVE_ADDR_3);
 	HDMI_WRITE(sc, AWIN_HDMI_DDC_SLAVE_ADDR_REG, val);
@@ -336,7 +359,7 @@ awin_hdmi_i2c_xfer_1_3(void *priv, i2c_a
 }
 
 static int
-awin_hdmi_i2c_xfer_1_4(void *priv, i2c_addr_t addr, uint8_t reg,
+awin_hdmi_i2c_xfer_1_4(void *priv, i2c_addr_t addr, uint8_t block, uint8_t reg,
     size_t len, int type, int flags)
 {
 	struct awin_hdmi_softc *sc = priv;
@@ -347,7 +370,7 @@ awin_hdmi_i2c_xfer_1_4(void *priv, i2c_a
 	val |= AWIN_A31_HDMI_DDC_FIFO_CTRL_RST;
 	HDMI_WRITE(sc, AWIN_A31_HDMI_DDC_FIFO_CTRL_REG, val);
 
-	val = __SHIFTIN(0, AWIN_A31_HDMI_DDC_SLAVE_ADDR_SEG_PTR);
+	val = __SHIFTIN(block, AWIN_A31_HDMI_DDC_SLAVE_ADDR_SEG_PTR);
 	val |= __SHIFTIN(0x60, AWIN_A31_HDMI_DDC_SLAVE_ADDR_DDC_CMD);
 	val |= __SHIFTIN(reg, AWIN_A31_HDMI_DDC_SLAVE_ADDR_OFF_ADR);
 	val |= __SHIFTIN(addr, AWIN_A31_HDMI_DDC_SLAVE_ADDR_DEV_ADR);
@@ -378,16 +401,18 @@ awin_hdmi_i2c_xfer_1_4(void *priv, i2c_a
 }
 
 static int
-awin_hdmi_i2c_xfer(void *priv, i2c_addr_t addr, uint8_t reg,
+awin_hdmi_i2c_xfer(void *priv, i2c_addr_t addr, uint8_t block, uint8_t reg,
     size_t len, int type, int flags)
 {
 	struct awin_hdmi_softc *sc = priv;
 	int rv;
 
 	if (HDMI_1_3_P(sc)) {
-		rv = awin_hdmi_i2c_xfer_1_3(priv, addr, reg, len, type, flags);
+		rv = awin_hdmi_i2c_xfer_1_3(priv, addr, block, reg, len,
+		    type, flags);
 	} else {
-		rv = awin_hdmi_i2c_xfer_1_4(priv, addr, reg, len, type, flags);
+		rv = awin_hdmi_i2c_xfer_1_4(priv, addr, block, reg, len,
+		    type, flags);
 	}
 
 	return rv;
@@ -468,12 +493,13 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 	char edid[128];
 	struct edid_info ei;
 	int retry = 4;
+	u_int display_mode;
 
 	memset(edid, 0, sizeof(edid));
 	memset(&ei, 0, sizeof(ei));
 
 	while (--retry > 0) {
-		if (ddc_read_edid(&sc->sc_ic, edid, sizeof(edid)) == 0)
+		if (!ddc_read_edid_block(&sc->sc_ic, edid, sizeof(edid), 0))
 			break;
 	}
 	if (retry == 0) {
@@ -489,6 +515,22 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 #endif
 	}
 
+	if (sc->sc_display_mode == DISPLAY_MODE_AUTO)
+		display_mode = awin_hdmi_get_display_mode(sc, &ei);
+	else
+		display_mode = sc->sc_display_mode;
+
+	const char *forced = sc->sc_display_mode == DISPLAY_MODE_AUTO ?
+	    "auto-detected" : "forced";
+	device_printf(sc->sc_dev, "%s mode (%s)\n",
+	    display_mode == DISPLAY_MODE_HDMI ? "HDMI" : "DVI", forced);
+
+	strlcpy(sc->sc_display_vendor, ei.edid_vendorname,
+	    sizeof(sc->sc_display_vendor));
+	strlcpy(sc->sc_display_product, ei.edid_productname,
+	    sizeof(sc->sc_display_product));
+	sc->sc_current_display_mode = display_mode;
+
 	mode = ei.edid_preferred_mode;
 	if (mode == NULL)
 		mode = pick_mode_by_ref(640, 480, 60);
@@ -500,8 +542,8 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 
 		awin_debe_set_videomode(mode);
 		awin_tcon_set_videomode(mode);
-		awin_hdmi_set_videomode(sc, mode);
-		awin_hdmi_set_audiomode(sc, mode);
+		awin_hdmi_set_videomode(sc, mode, display_mode);
+		awin_hdmi_set_audiomode(sc, mode, display_mode);
 		awin_debe_enable(true);
 		delay(20000);
 		awin_tcon_enable(true);
@@ -510,6 +552,90 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 	}
 }
 
+static u_int
+awin_hdmi_get_display_mode(struct awin_hdmi_softc *sc,
+    const struct edid_info *ei)
+{
+	char edid[128];
+	bool found_hdmi = false;
+	unsigned int n, p;
+
+	/*
+	 * Scan through extension blocks, looking for a CEA-861-D v3
+	 * block. If an HDMI Vendor-Specific Data Block (HDMI VSDB) is
+	 * found in that, assume HDMI mode.
+	 */
+	for (n = 1; n <= MIN(ei->edid_ext_block_count, 4); n++) {
+		if (ddc_read_edid_block(&sc->sc_ic, edid, sizeof(edid), n)) {
+#ifdef AWIN_HDMI_DEBUG
+			device_printf(sc->sc_dev,
+			    "Failed to read EDID block %d\n", n);
+#endif
+			break;
+		}
+
+#ifdef AWIN_HDMI_DEBUG
+		device_printf(sc->sc_dev, "EDID block #%d:\n", n);
+#endif
+
+		const uint8_t tag = edid[0];
+		const uint8_t rev = edid[1];
+		const uint8_t off = edid[2];
+
+#ifdef AWIN_HDMI_DEBUG
+		device_printf(sc->sc_dev, "  Tag %d, Revision %d, Offset %d\n",
+		    tag, rev, off);
+		device_printf(sc->sc_dev, "  Flags: 0x%02x\n", edid[3]);
+#endif
+
+		/* We are looking for a CEA-861-D tag (02h) with revision 3 */
+		if (tag != 0x02 || rev != 3)
+			continue;
+		/*
+		 * CEA data block collection starts at byte 4, so the
+		 * DTD blocks must start after it.
+		 */
+		if (off <= 4)
+			continue;
+
+		/* Parse the CEA data blocks */
+		for (p = 4; p < off;) {
+			const uint8_t btag = (edid[p] >> 5) & 0x7;
+			const uint8_t blen = edid[p] & 0x1f;
+
+#ifdef AWIN_HDMI_DEBUG
+			device_printf(sc->sc_dev, "  CEA data block @ %d\n", p);
+			device_printf(sc->sc_dev, "    Tag %d, Length %d\n",
+			    btag, blen);
+#endif
+
+			/* Make sure the length is sane */
+			if (p + blen + 1 > off)
+				break;
+			/* Looking for a VSDB tag */
+			if (btag != 3)
+				goto next_block;
+			/* HDMI VSDB is at least 5 bytes long */
+			if (blen < 5)
+				goto next_block;
+
+#ifdef AWIN_HDMI_DEBUG
+			device_printf(sc->sc_dev, "    ID: %02x%02x%02x\n",
+			    edid[p + 1], edid[p + 2], edid[p + 3]);
+#endif
+
+			/* HDMI 24-bit IEEE registration ID is 0x000C03 */
+			if (memcmp(&edid[p + 1], "\x03\x0c\x00", 3) == 0)
+				found_hdmi = true;
+
+next_block:
+			p += (1 + blen);
+		}
+	}
+
+	return found_hdmi ? DISPLAY_MODE_HDMI : DISPLAY_MODE_DVI;
+}
+
 static void
 awin_hdmi_video_enable(struct awin_hdmi_softc *sc, bool enable)
 {
@@ -538,7 +664,7 @@ awin_hdmi_video_enable(struct awin_hdmi_
 
 static void
 awin_hdmi_set_videomode(struct awin_hdmi_softc *sc,
-    const struct videomode *mode)
+    const struct videomode *mode, u_int display_mode)
 {
 	uint32_t val;
 	const u_int dblscan_p = !!(mode->flags & VID_DBLSCAN);
@@ -603,13 +729,20 @@ awin_hdmi_set_videomode(struct awin_hdmi
 #endif
 
 	val = HDMI_READ(sc, AWIN_HDMI_VID_CTRL_REG);
-	val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_HDMI_MODE_HDMI,
-			 AWIN_HDMI_VID_CTRL_HDMI_MODE);
-	val &= ~AWIN_HDMI_VID_CTRL_OUTPUT_FMT;
+	val &= ~AWIN_HDMI_VID_CTRL_HDMI_MODE;
+	if (display_mode == DISPLAY_MODE_DVI) {
+		val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_HDMI_MODE_DVI,
+				 AWIN_HDMI_VID_CTRL_HDMI_MODE);
+	} else {
+		val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_HDMI_MODE_HDMI,
+				 AWIN_HDMI_VID_CTRL_HDMI_MODE);
+	}
+	val &= ~AWIN_HDMI_VID_CTRL_REPEATER_SEL;
 	if (dblscan_p) {
 		val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_REPEATER_SEL_2X,
 				 AWIN_HDMI_VID_CTRL_REPEATER_SEL);
 	}
+	val &= ~AWIN_HDMI_VID_CTRL_OUTPUT_FMT;
 	if (interlace_p) {
 		val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_OUTPUT_FMT_INTERLACE,
 				 AWIN_HDMI_VID_CTRL_OUTPUT_FMT);
@@ -658,7 +791,7 @@ awin_hdmi_set_videomode(struct awin_hdmi
 
 static void
 awin_hdmi_set_audiomode(struct awin_hdmi_softc *sc,
-    const struct videomode *mode)
+    const struct videomode *mode, u_int display_mode)
 {
 	uint32_t cts, n, val;
 
@@ -675,6 +808,11 @@ awin_hdmi_set_audiomode(struct awin_hdmi
 		val = HDMI_READ(sc, AWIN_HDMI_AUD_CTRL_REG);
 	} while (val & AWIN_HDMI_AUD_CTRL_RST);
 
+	/* No audio support in DVI mode */
+	if (display_mode != DISPLAY_MODE_HDMI) {
+		return;
+	}
+
 	/* DMA & FIFO control */
 	val = HDMI_READ(sc, AWIN_HDMI_ADMA_CTRL_REG);
 	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
@@ -740,7 +878,6 @@ awin_hdmi_hpd(struct awin_hdmi_softc *sc
 	if (sc->sc_connected == con)
 		return;
 
-	sc->sc_connected = con;
 	if (con) {
 		device_printf(sc->sc_dev, "display connected\n");
 		awin_hdmi_read_edid(sc);
@@ -748,6 +885,8 @@ awin_hdmi_hpd(struct awin_hdmi_softc *sc
 		device_printf(sc->sc_dev, "display disconnected\n");
 		awin_tcon_set_videomode(NULL);
 	}
+
+	sc->sc_connected = con;
 }
 
 static void
@@ -780,6 +919,32 @@ awin_hdmi_intr(void *priv)
 }
 #endif
 
+void
+awin_hdmi_get_info(struct awin_hdmi_info *info)
+{
+	struct awin_hdmi_softc *sc;
+	device_t dev;
+
+	memset(info, 0, sizeof(*info));
+
+	dev = device_find_by_driver_unit("awinhdmi", 0);
+	if (dev == NULL) {
+		info->display_connected = false;
+		return;
+	}
+	sc = device_private(dev);
+
+	info->display_connected = sc->sc_connected;
+	if (info->display_connected) {
+		strlcpy(info->display_vendor, sc->sc_display_vendor,
+		    sizeof(info->display_vendor));
+		strlcpy(info->display_product, sc->sc_display_product,
+		    sizeof(info->display_product));
+		info->display_hdmimode =
+		    sc->sc_current_display_mode == DISPLAY_MODE_HDMI;
+	}
+}
+
 #if defined(DDB)
 void
 awin_hdmi_dump_regs(void)

Index: src/sys/arch/arm/allwinner/awin_hdmiaudio.c
diff -u src/sys/arch/arm/allwinner/awin_hdmiaudio.c:1.3.2.2 src/sys/arch/arm/allwinner/awin_hdmiaudio.c:1.3.2.3
--- src/sys/arch/arm/allwinner/awin_hdmiaudio.c:1.3.2.2	Fri Nov 14 13:37:39 2014
+++ src/sys/arch/arm/allwinner/awin_hdmiaudio.c	Tue Nov 18 18:19:09 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_hdmiaudio.c,v 1.3.2.2 2014/11/14 13:37:39 martin Exp $ */
+/* $NetBSD: awin_hdmiaudio.c,v 1.3.2.3 2014/11/18 18:19:09 snj Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_hdmiaudio.c,v 1.3.2.2 2014/11/14 13:37:39 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_hdmiaudio.c,v 1.3.2.3 2014/11/18 18:19:09 snj Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -538,14 +538,35 @@ static int
 awin_hdmiaudio_getdev(void *priv, struct audio_device *audiodev)
 {
 	struct awin_hdmiaudio_softc *sc = priv;
+	struct awin_hdmi_info info;
+	const char *vendor = NULL, *product = NULL;
 
 	const int vmaj = __SHIFTOUT(sc->sc_ver, AWIN_HDMI_VERSION_ID_H);
 	const int vmin = __SHIFTOUT(sc->sc_ver, AWIN_HDMI_VERSION_ID_L);
 
-	snprintf(audiodev->name, sizeof(audiodev->name), "Allwinner");
-	snprintf(audiodev->version, sizeof(audiodev->version),
+	awin_hdmi_get_info(&info);
+
+	if (info.display_connected && info.display_hdmimode) {
+		if (strlen(info.display_vendor) > 0 &&
+		    strlen(info.display_product) > 0) {
+			vendor = info.display_vendor;
+			product = info.display_product;
+		} else {
+			vendor = "HDMI";
+			product = "";
+		}
+	} else if (info.display_connected) {
+		vendor = "DVI";
+		product = "(unsupported)";
+	} else {
+		vendor = "HDMI";
+		product = "(disconnected)";
+	}
+
+	strlcpy(audiodev->name, vendor, sizeof(audiodev->name));
+	strlcpy(audiodev->version, product, sizeof(audiodev->version));
+	snprintf(audiodev->config, sizeof(audiodev->config),
 	    "HDMI %d.%d", vmaj, vmin);
-	snprintf(audiodev->config, sizeof(audiodev->config), "awin_hdmiaudio");
 	return 0;
 }
 
@@ -574,11 +595,16 @@ awin_hdmiaudio_trigger_output(void *priv
 {
 	struct awin_hdmiaudio_softc *sc = priv;
 	struct awin_hdmiaudio_dma *dma;
+	struct awin_hdmi_info info;
 	bus_addr_t pstart;
 	bus_size_t psize;
 	uint32_t dmacfg;
 	int error;
 
+	awin_hdmi_get_info(&info);
+	if (info.display_connected == false || info.display_hdmimode == false)
+		return ENXIO;
+
 	pstart = 0;
 	psize = (uintptr_t)end - (uintptr_t)start;
 

Index: src/sys/arch/arm/allwinner/awin_var.h
diff -u src/sys/arch/arm/allwinner/awin_var.h:1.10.2.4 src/sys/arch/arm/allwinner/awin_var.h:1.10.2.5
--- src/sys/arch/arm/allwinner/awin_var.h:1.10.2.4	Sat Nov 15 11:31:40 2014
+++ src/sys/arch/arm/allwinner/awin_var.h	Tue Nov 18 18:19:09 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_var.h,v 1.10.2.4 2014/11/15 11:31:40 martin Exp $ */
+/* $NetBSD: awin_var.h,v 1.10.2.5 2014/11/18 18:19:09 snj Exp $ */
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -132,6 +132,15 @@ void	awin_tcon_enable(bool);
 void	awin_debe_set_videomode(const struct videomode *);
 void	awin_debe_enable(bool);
 int	awin_debe_ioctl(device_t, u_long, void *);
+
+struct awin_hdmi_info {
+	bool	display_connected;
+	char	display_vendor[16];
+	char	display_product[16];
+	bool	display_hdmimode;
+};
+void	awin_hdmi_get_info(struct awin_hdmi_info *);
+
 void	awin_fb_set_videomode(device_t, u_int, u_int);
 void	awin_fb_ddb_trap_callback(int);
 

Index: src/sys/arch/evbarm/awin/awin_machdep.c
diff -u src/sys/arch/evbarm/awin/awin_machdep.c:1.8.2.7 src/sys/arch/evbarm/awin/awin_machdep.c:1.8.2.8
--- src/sys/arch/evbarm/awin/awin_machdep.c:1.8.2.7	Fri Nov 14 22:23:28 2014
+++ src/sys/arch/evbarm/awin/awin_machdep.c	Tue Nov 18 18:19:09 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: awin_machdep.c,v 1.8.2.7 2014/11/14 22:23:28 martin Exp $ */
+/*	$NetBSD: awin_machdep.c,v 1.8.2.8 2014/11/18 18:19:09 snj Exp $ */
 
 /*
  * Machine dependent functions for kernel setup for TI OSK5912 board.
@@ -125,7 +125,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_machdep.c,v 1.8.2.7 2014/11/14 22:23:28 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_machdep.c,v 1.8.2.8 2014/11/18 18:19:09 snj Exp $");
 
 #include "opt_machdep.h"
 #include "opt_ddb.h"
@@ -769,6 +769,20 @@ awin_device_register(device_t self, void
 		}
 	}
 
+	if (device_is_a(self, "awinhdmi")) {
+		char *display_mode;
+		if (get_bootconf_option(boot_args, "hdmi.forcemode",
+		    BOOTOPT_TYPE_STRING, &display_mode)) {
+			if (strcasecmp(display_mode, "hdmi") == 0) {
+				prop_dictionary_set_cstring(dict,
+				    "display-mode", "hdmi");
+			} else if (strcasecmp(display_mode, "dvi") == 0) {
+				prop_dictionary_set_cstring(dict,
+				    "display-mode", "dvi");
+			}
+		}
+	}
+
 #if NGENFB > 0
 	if (device_is_a(self, "genfb")) {
 #ifdef DDB

Index: src/sys/dev/i2c/ddc.c
diff -u src/sys/dev/i2c/ddc.c:1.3 src/sys/dev/i2c/ddc.c:1.3.62.1
--- src/sys/dev/i2c/ddc.c:1.3	Sun May  4 15:26:29 2008
+++ src/sys/dev/i2c/ddc.c	Tue Nov 18 18:19:10 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: ddc.c,v 1.3 2008/05/04 15:26:29 xtraeme Exp $ */
+/* $NetBSD: ddc.c,v 1.3.62.1 2014/11/18 18:19:10 snj Exp $ */
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -32,7 +32,7 @@
  */ 
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ddc.c,v 1.3 2008/05/04 15:26:29 xtraeme Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ddc.c,v 1.3.62.1 2014/11/18 18:19:10 snj Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -91,12 +91,18 @@ ddc_attach(device_t parent, device_t sel
 int
 ddc_read_edid(i2c_tag_t tag, uint8_t *dest, size_t len)
 {
+	return ddc_read_edid_block(tag, dest, len, DDC_EDID_START);
+}
+
+int
+ddc_read_edid_block(i2c_tag_t tag, uint8_t *dest, size_t len, uint8_t block)
+{
 	uint8_t		wbuf[2];
 
 	if (iic_acquire_bus(tag, I2C_F_POLL) != 0)
 		return -1;
 
-	wbuf[0] = DDC_EDID_START;	/* start address */
+	wbuf[0] = block;	/* start address */
 
 	if (iic_exec(tag, I2C_OP_READ_WITH_STOP, DDC_ADDR, wbuf, 1, dest,
 		len, I2C_F_POLL)) {

Index: src/sys/dev/i2c/ddcvar.h
diff -u src/sys/dev/i2c/ddcvar.h:1.1 src/sys/dev/i2c/ddcvar.h:1.1.138.1
--- src/sys/dev/i2c/ddcvar.h:1.1	Tue Apr 25 21:18:16 2006
+++ src/sys/dev/i2c/ddcvar.h	Tue Nov 18 18:19:10 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: ddcvar.h,v 1.1 2006/04/25 21:18:16 gdamore Exp $	*/
+/*	$NetBSD: ddcvar.h,v 1.1.138.1 2014/11/18 18:19:10 snj Exp $	*/
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -40,5 +40,6 @@
  * autoconfiguration framework.
  */
 int ddc_read_edid(i2c_tag_t, u_int8_t *, size_t);
+int ddc_read_edid_block(i2c_tag_t, u_int8_t *, size_t, uint8_t);
 
 #endif /* _DEV_I2C_DDCVAR_H_ */

Index: src/sys/dev/videomode/edid.c
diff -u src/sys/dev/videomode/edid.c:1.12 src/sys/dev/videomode/edid.c:1.12.12.1
--- src/sys/dev/videomode/edid.c:1.12	Fri Feb  8 16:35:10 2013
+++ src/sys/dev/videomode/edid.c	Tue Nov 18 18:19:10 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: edid.c,v 1.12 2013/02/08 16:35:10 skrll Exp $ */
+/* $NetBSD: edid.c,v 1.12.12.1 2014/11/18 18:19:10 snj Exp $ */
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -32,7 +32,7 @@
  */ 
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.12 2013/02/08 16:35:10 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.12.12.1 2014/11/18 18:19:10 snj Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -286,6 +286,8 @@ edid_print(struct edid_info *edid)
 		    DIVIDE(DIVIDE(edid->edid_preferred_mode->dot_clock * 1000,
 		    edid->edid_preferred_mode->htotal),
 		    edid->edid_preferred_mode->vtotal));
+
+	printf("Number of extension blocks: %d\n", edid->edid_ext_block_count);
 }
 
 static const struct videomode *
@@ -569,6 +571,8 @@ edid_parse(uint8_t *data, struct edid_in
 	edid->edid_chroma.ec_whitex = EDID_CHROMA_WHITEX(data);
 	edid->edid_chroma.ec_whitey = EDID_CHROMA_WHITEY(data);
 
+	edid->edid_ext_block_count = EDID_EXT_BLOCK_COUNT(data);
+
 	/* lookup established modes */
 	edid->edid_nmodes = 0;
 	edid->edid_preferred_mode = NULL;

Index: src/sys/dev/videomode/edidreg.h
diff -u src/sys/dev/videomode/edidreg.h:1.3 src/sys/dev/videomode/edidreg.h:1.3.30.1
--- src/sys/dev/videomode/edidreg.h:1.3	Wed Mar 30 18:49:56 2011
+++ src/sys/dev/videomode/edidreg.h	Tue Nov 18 18:19:10 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: edidreg.h,v 1.3 2011/03/30 18:49:56 jdc Exp $	*/
+/*	$NetBSD: edidreg.h,v 1.3.30.1 2014/11/18 18:19:10 snj Exp $	*/
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -250,4 +250,6 @@
 #define	EDID_DESC_STD_TIMING_START		5
 #define	EDID_DESC_STD_TIMING_COUNT		6
 
+#define	EDID_EXT_BLOCK_COUNT(ptr)		((ptr)[126])
+
 #endif /* _DEV_VIDEOMODE_EDIDREG_H */

Index: src/sys/dev/videomode/edidvar.h
diff -u src/sys/dev/videomode/edidvar.h:1.2 src/sys/dev/videomode/edidvar.h:1.2.138.1
--- src/sys/dev/videomode/edidvar.h:1.2	Thu May 11 19:05:41 2006
+++ src/sys/dev/videomode/edidvar.h	Tue Nov 18 18:19:10 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: edidvar.h,v 1.2 2006/05/11 19:05:41 gdamore Exp $	*/
+/*	$NetBSD: edidvar.h,v 1.2.138.1 2014/11/18 18:19:10 snj Exp $	*/
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -75,6 +75,7 @@ struct edid_info {
 	uint8_t		edid_max_vsize;		/* in cm */
 	uint8_t		edid_gamma;
 	uint8_t		edid_features;
+	uint8_t		edid_ext_block_count;
 
 	int			edid_have_range;
 	struct edid_range	edid_range;

Reply via email to