Module Name:    src
Committed By:   jmcneill
Date:           Sun Nov  9 14:10:54 UTC 2014

Modified Files:
        src/sys/arch/arm/allwinner: awin_board.c awin_hdmi.c awin_io.c
            awin_reg.h awin_var.h files.awin
Added Files:
        src/sys/arch/arm/allwinner: awin_debe.c awin_fb.c awin_tcon.c

Log Message:
awin framebuffer support, not sure if this works yet because hdmi is not 
working yet


To generate a diff of this commit:
cvs rdiff -u -r1.25 -r1.26 src/sys/arch/arm/allwinner/awin_board.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/allwinner/awin_debe.c \
    src/sys/arch/arm/allwinner/awin_fb.c \
    src/sys/arch/arm/allwinner/awin_tcon.c
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/allwinner/awin_hdmi.c
cvs rdiff -u -r1.26 -r1.27 src/sys/arch/arm/allwinner/awin_io.c
cvs rdiff -u -r1.45 -r1.46 src/sys/arch/arm/allwinner/awin_reg.h
cvs rdiff -u -r1.19 -r1.20 src/sys/arch/arm/allwinner/awin_var.h
cvs rdiff -u -r1.21 -r1.22 src/sys/arch/arm/allwinner/files.awin

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_board.c
diff -u src/sys/arch/arm/allwinner/awin_board.c:1.25 src/sys/arch/arm/allwinner/awin_board.c:1.26
--- src/sys/arch/arm/allwinner/awin_board.c:1.25	Wed Oct 29 14:14:14 2014
+++ src/sys/arch/arm/allwinner/awin_board.c	Sun Nov  9 14:10:54 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: awin_board.c,v 1.25 2014/10/29 14:14:14 skrll Exp $	*/
+/*	$NetBSD: awin_board.c,v 1.26 2014/11/09 14:10:54 jmcneill Exp $	*/
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -36,7 +36,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: awin_board.c,v 1.25 2014/10/29 14:14:14 skrll Exp $");
+__KERNEL_RCSID(1, "$NetBSD: awin_board.c,v 1.26 2014/11/09 14:10:54 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -323,18 +323,25 @@ awin_pll6_enable(void)
 	 */
 	const uint32_t ocfg = bus_space_read_4(bst, bsh,
 	    AWIN_CCM_OFFSET + AWIN_PLL6_CFG_REG);
-	const u_int k = __SHIFTOUT(ocfg, AWIN_PLL_CFG_FACTOR_K);
 
 	/*
 	 * Output freq is 24MHz * n * k / m / 6.
 	 * To get to 100MHz, k & m must be equal and n must be 25.
 	 */
 	uint32_t ncfg = ocfg;
-	ncfg &= ~(AWIN_PLL_CFG_FACTOR_M|AWIN_PLL_CFG_FACTOR_N);
 	ncfg &= ~(AWIN_PLL_CFG_BYPASS);
-	ncfg |= __SHIFTIN(k, AWIN_PLL_CFG_FACTOR_M);
-	ncfg |= __SHIFTIN(25, AWIN_PLL_CFG_FACTOR_N);
-	ncfg |= AWIN_PLL_CFG_ENABLE | AWIN_PLL6_CFG_SATA_CLK_EN;
+	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		ncfg &= ~(AWIN_PLL_CFG_FACTOR_N|AWIN_PLL_CFG_FACTOR_K);
+		ncfg |= __SHIFTIN(1, AWIN_PLL_CFG_FACTOR_K);
+		ncfg |= __SHIFTIN(24, AWIN_PLL_CFG_FACTOR_N);
+	} else {
+		const u_int k = __SHIFTOUT(ocfg, AWIN_PLL_CFG_FACTOR_K);
+		ncfg &= ~(AWIN_PLL_CFG_FACTOR_M|AWIN_PLL_CFG_FACTOR_N);
+		ncfg |= __SHIFTIN(k, AWIN_PLL_CFG_FACTOR_M);
+		ncfg |= __SHIFTIN(25, AWIN_PLL_CFG_FACTOR_N);
+		ncfg |= AWIN_PLL6_CFG_SATA_CLK_EN;
+	}
+	ncfg |= AWIN_PLL_CFG_ENABLE;
 	if (ncfg != ocfg) {
 		bus_space_write_4(bst, bsh,
 		    AWIN_CCM_OFFSET + AWIN_PLL6_CFG_REG, ncfg);
@@ -387,6 +394,40 @@ awin_pll2_enable(void)
 }
 
 void
+awin_pll3_enable(void)
+{
+	bus_space_tag_t bst = &awin_bs_tag;
+	bus_space_handle_t bsh = awin_core_bsh;
+
+	/*
+	 * HDMI needs PLL3 to be 29700000 Hz
+	 */
+	const uint32_t ocfg = bus_space_read_4(bst, bsh,
+	    AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG);
+
+	uint32_t ncfg = ocfg;
+
+	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		ncfg &= ~AWIN_A31_PLL3_CFG_MODE;
+		ncfg &= ~AWIN_A31_PLL3_CFG_MODE_SEL;
+		ncfg |= AWIN_A31_PLL3_CFG_FRAC_CLK_OUT;
+		/* 24MHz*N/M - for 29.7MHz, N=99, M=8 */
+		ncfg |= __SHIFTIN(98, AWIN_A31_PLL3_CFG_FACTOR_N);
+		ncfg |= __SHIFTIN(7, AWIN_A31_PLL3_CFG_PREDIV_M);
+		ncfg |= AWIN_PLL_CFG_ENABLE;
+	} else {
+		ncfg &= ~AWIN_PLL3_MODE_SEL;
+		ncfg |= AWIN_PLL3_FRAC_SET;
+		ncfg |= AWIN_PLL_CFG_ENABLE;
+	}
+
+	if (ncfg != ocfg) {
+		bus_space_write_4(bst, bsh,
+		    AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG, ncfg);
+	}
+}
+
+void
 awin_pll7_enable(void)
 {
 	bus_space_tag_t bst = &awin_bs_tag;
@@ -401,8 +442,14 @@ awin_pll7_enable(void)
 	uint32_t ncfg = ocfg;
 
 	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		ncfg &= ~AWIN_A31_PLL7_CFG_MODE;
 		ncfg &= ~AWIN_A31_PLL7_CFG_MODE_SEL;
 		ncfg |= AWIN_A31_PLL7_CFG_FRAC_CLK_OUT;
+		/* 24MHz*N/M - for 29.7MHz, N=99, M=8 */
+		ncfg &= ~AWIN_A31_PLL7_CFG_FACTOR_N;
+		ncfg &= ~AWIN_A31_PLL7_CFG_PREDIV_M;
+		ncfg |= __SHIFTIN(98, AWIN_A31_PLL7_CFG_FACTOR_N);
+		ncfg |= __SHIFTIN(7, AWIN_A31_PLL7_CFG_PREDIV_M);
 		ncfg |= AWIN_PLL_CFG_ENABLE;
 	} else {
 		ncfg &= ~AWIN_PLL7_MODE_SEL;
@@ -415,3 +462,39 @@ awin_pll7_enable(void)
 		    AWIN_CCM_OFFSET + AWIN_PLL7_CFG_REG, ncfg);
 	}
 }
+
+void
+awin_pll3_set_rate(uint32_t rate)
+{
+	bus_space_tag_t bst = &awin_bs_tag;
+	bus_space_handle_t bsh = awin_core_bsh;
+
+	const uint32_t ocfg = bus_space_read_4(bst, bsh,
+	    AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG);
+
+	uint32_t ncfg = ocfg;
+	if (rate == 0) {
+		ncfg &= ~AWIN_PLL_CFG_ENABLE;
+	} else {
+		if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+			unsigned int m = rate / 3000000;
+			ncfg |= AWIN_PLL3_MODE_SEL;
+			ncfg &= ~AWIN_PLL3_FACTOR_M;
+			ncfg |= __SHIFTIN(m, AWIN_PLL3_FACTOR_M);
+		} else {
+			unsigned int m = 8;
+			unsigned int n = rate / 3000000;
+			ncfg |= AWIN_A31_PLL3_CFG_MODE_SEL;
+			ncfg &= ~AWIN_A31_PLL3_CFG_FACTOR_N;
+			ncfg |= __SHIFTIN(n - 1, AWIN_A31_PLL3_CFG_FACTOR_N);
+			ncfg &= ~AWIN_A31_PLL3_CFG_PREDIV_M;
+			ncfg |= __SHIFTIN(m - 1, AWIN_A31_PLL3_CFG_PREDIV_M);
+		}
+		ncfg |= AWIN_PLL_CFG_ENABLE;
+	}
+
+	if (ncfg != ocfg) {
+		bus_space_write_4(bst, bsh,
+		    AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG, ncfg);
+	}
+}

Index: src/sys/arch/arm/allwinner/awin_hdmi.c
diff -u src/sys/arch/arm/allwinner/awin_hdmi.c:1.2 src/sys/arch/arm/allwinner/awin_hdmi.c:1.3
--- src/sys/arch/arm/allwinner/awin_hdmi.c:1.2	Sat Nov  8 00:31:54 2014
+++ src/sys/arch/arm/allwinner/awin_hdmi.c	Sun Nov  9 14:10:54 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_hdmi.c,v 1.2 2014/11/08 00:31:54 jmcneill Exp $ */
+/* $NetBSD: awin_hdmi.c,v 1.3 2014/11/09 14:10:54 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <[email protected]>
@@ -26,10 +26,14 @@
  * SUCH DAMAGE.
  */
 
-#define AWIN_HDMI_PLL	7	/* PLL7 or PLL3 */
+#define AWIN_HDMI_DEBUG
+
+#include "opt_ddb.h"
+
+#define AWIN_HDMI_PLL	3	/* PLL7 or PLL3 */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.2 2014/11/08 00:31:54 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.3 2014/11/09 14:10:54 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -87,10 +91,19 @@ static int	awin_hdmi_i2c_xfer(void *, i2
 static int	awin_hdmi_i2c_reset(struct awin_hdmi_softc *, int);
 
 static void	awin_hdmi_enable(struct awin_hdmi_softc *);
-static void	awin_hdmi_connect(struct awin_hdmi_softc *);
 static void	awin_hdmi_read_edid(struct awin_hdmi_softc *);
+static void	awin_hdmi_set_videomode(struct awin_hdmi_softc *,
+					struct videomode *);
+static void	awin_hdmi_set_audiomode(struct awin_hdmi_softc *,
+					struct videomode *);
+static void	awin_hdmi_hpd(struct awin_hdmi_softc *);
 static void	awin_hdmi_thread(void *);
+#if 0
 static int	awin_hdmi_intr(void *);
+#endif
+#if defined(DDB)
+void		awin_hdmi_dump_regs(void);
+#endif
 
 CFATTACH_DECL_NEW(awin_hdmi, sizeof(struct awin_hdmi_softc),
 	awin_hdmi_match, awin_hdmi_attach, NULL, NULL);
@@ -133,7 +146,11 @@ awin_hdmi_attach(device_t parent, device
 		    AWIN_A31_AHB_RESET1_REG, AWIN_A31_AHB_RESET1_HDMI_RST, 0);
 	}
 
+#if AWIN_HDMI_PLL == 7
 	clk = __SHIFTIN(AWIN_HDMI_CLK_SRC_SEL_PLL7, AWIN_HDMI_CLK_SRC_SEL);
+#else
+	clk = __SHIFTIN(AWIN_HDMI_CLK_SRC_SEL_PLL3, AWIN_HDMI_CLK_SRC_SEL);
+#endif
 	clk |= __SHIFTIN(0, AWIN_HDMI_CLK_DIV_RATIO_M);
 	clk |= AWIN_CLK_ENABLE;
 	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
@@ -159,6 +176,7 @@ awin_hdmi_attach(device_t parent, device
 		sc->sc_i2c_blklen = 16;
 	}
 
+#if 0
 	sc->sc_ih = intr_establish(loc->loc_intr, IPL_SCHED, IST_LEVEL,
 	    awin_hdmi_intr, sc);
 	if (sc->sc_ih == NULL) {
@@ -167,11 +185,16 @@ awin_hdmi_attach(device_t parent, device
 		return;
 	}
 	aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intr);
+#endif
 
 	awin_hdmi_i2c_init(sc);
 
 	awin_hdmi_enable(sc);
 
+	delay(50000);
+
+	awin_hdmi_hpd(sc);
+
 	kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, awin_hdmi_thread, sc,
 	    &sc->sc_thread, "%s", device_xname(sc->sc_dev));
 }
@@ -340,7 +363,10 @@ awin_hdmi_i2c_xfer_1_4(void *priv, i2c_a
 		val = HDMI_READ(sc, AWIN_A31_HDMI_DDC_CTRL_REG);
 		if ((val & AWIN_A31_HDMI_DDC_CTRL_ACCESS_CMD_START) == 0)
 			break;
-		kpause("hdmiddc", false, mstohz(10), &sc->sc_ic_lock);
+		if (cold)
+			delay(1000);
+		else
+			kpause("hdmiddc", false, mstohz(10), &sc->sc_ic_lock);
 	}
 	if (retry == 0)
 		return ETIMEDOUT;
@@ -417,54 +443,17 @@ awin_hdmi_enable(struct awin_hdmi_softc 
 {
 	HDMI_WRITE(sc, AWIN_HDMI_CTRL_REG, AWIN_HDMI_CTRL_MODULE_EN);
 	if (awin_chip_id() == AWIN_CHIP_ID_A20) {
-		HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL0_REG,
-		    AWIN_HDMI_PAD_CTRL0_BIAS |
-		    AWIN_HDMI_PAD_CTRL0_LDOCEN |
-		    AWIN_HDMI_PAD_CTRL0_LD0DEN);
-	}
-}
-
-static void
-awin_hdmi_connect(struct awin_hdmi_softc *sc)
-{
-	HDMI_WRITE(sc, AWIN_HDMI_CTRL_REG, AWIN_HDMI_CTRL_MODULE_EN);
-
-	HDMI_WRITE(sc, AWIN_HDMI_PLL_CTRL_REG,
-	    AWIN_HDMI_PLL_CTRL_PLL_EN |
-	    AWIN_HDMI_PLL_CTRL_BWS |
-	    AWIN_HDMI_PLL_CTRL_HV_IS_33 |
-	    AWIN_HDMI_PLL_CTRL_LDO1_EN |
-	    AWIN_HDMI_PLL_CTRL_LDO2_EN |
-	    AWIN_HDMI_PLL_CTRL_SDIV2 |
-	    __SHIFTIN(0x4, AWIN_HDMI_PLL_CTRL_VCO_GAIN) |
-	    __SHIFTIN(0x7, AWIN_HDMI_PLL_CTRL_S) |
-	    __SHIFTIN(0xf, AWIN_HDMI_PLL_CTRL_CP_S) |
-	    __SHIFTIN(0x7, AWIN_HDMI_PLL_CTRL_CS) |
-	    __SHIFTIN(0xf, AWIN_HDMI_PLL_CTRL_PREDIV) |
-	    __SHIFTIN(0x8, AWIN_HDMI_PLL_CTRL_VCO_S));
-
-	HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL0_REG,
-	    AWIN_HDMI_PAD_CTRL0_BIAS |
-	    AWIN_HDMI_PAD_CTRL0_LDOCEN |
-	    AWIN_HDMI_PAD_CTRL0_LD0DEN |
-	    AWIN_HDMI_PAD_CTRL0_PWENC |
-	    AWIN_HDMI_PAD_CTRL0_PWEND |
-	    AWIN_HDMI_PAD_CTRL0_PWENG |
-	    AWIN_HDMI_PAD_CTRL0_CKEN |
-	    AWIN_HDMI_PAD_CTRL0_TXEN);
-
-	HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL1_REG,
-	    AWIN_HDMI_PAD_CTRL1_AMP_OPT |
-	    AWIN_HDMI_PAD_CTRL1_AMPCK_OPT |
-	    AWIN_HDMI_PAD_CTRL1_EMP_OPT |
-	    AWIN_HDMI_PAD_CTRL1_EMPCK_OPT |
-	    AWIN_HDMI_PAD_CTRL1_REG_DEN |
-	    AWIN_HDMI_PAD_CTRL1_REG_DENCK |
-	    __SHIFTIN(0x2, AWIN_HDMI_PAD_CTRL1_REG_EMP) |
-	    __SHIFTIN(0x1, AWIN_HDMI_PAD_CTRL1_REG_CKSS) |
-	    __SHIFTIN(0x4, AWIN_HDMI_PAD_CTRL1_REG_AMP));
-
-	HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, 0);
+		HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL0_REG, 0xfe800000);
+		HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL1_REG, 0x00d8c830);
+	} else if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL0_REG, 0x7e80000f);
+		HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL1_REG, 0x01ded030);
+	}
+#if AWIN_HDMI_PLL == 7
+	HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (1<<21));
+#elif AWIN_HDMI_PLL == 3
+	HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (0<<21));
+#endif
 }
 
 static void
@@ -484,35 +473,187 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 	}
 
 	edid_parse(edid, &ei);
+#ifdef AWIN_HDMI_DEBUG
 	edid_print(&ei);
+#endif
+
+	awin_debe_set_videomode(ei.edid_preferred_mode);
+	awin_tcon_set_videomode(ei.edid_preferred_mode);
+
+	if (ei.edid_preferred_mode != NULL) {
+		delay(10000);
+		awin_hdmi_set_videomode(sc, ei.edid_preferred_mode);
+		awin_hdmi_set_audiomode(sc, ei.edid_preferred_mode);
+	}
 }
 
 static void
-awin_hdmi_thread(void *priv)
+awin_hdmi_set_videomode(struct awin_hdmi_softc *sc, struct videomode *mode)
 {
-	struct awin_hdmi_softc *sc = priv;
+	uint32_t val;
+	const u_int dblscan_p = !!(mode->flags & VID_DBLSCAN);
+	const u_int interlace_p = !!(mode->flags & VID_INTERLACE);
+	const u_int phsync_p = !!(mode->flags & VID_PHSYNC);
+	const u_int pvsync_p = !!(mode->flags & VID_PVSYNC);
+	const u_int hbp = mode->htotal - mode->hsync_start;
+	const u_int hfp = mode->hsync_start - mode->hdisplay;
+	const u_int hspw = mode->hsync_end - mode->hsync_start;
+	const u_int vbp = mode->vtotal - mode->vsync_start;
+	const u_int vfp = mode->vsync_start - mode->vdisplay;
+	const u_int vspw = mode->vsync_end - mode->vsync_start;
+
+#ifdef AWIN_HDMI_DEBUG
+	device_printf(sc->sc_dev,
+	    "dblscan %d, interlace %d, phsync %d, pvsync %d\n",
+	    dblscan_p, interlace_p, phsync_p, pvsync_p);
+	device_printf(sc->sc_dev, "h: %u %u %u %u\n",
+	    mode->hdisplay, hbp, hfp, hspw);
+	device_printf(sc->sc_dev, "v: %u %u %u %u\n",
+	    mode->vdisplay, vbp, vfp, vspw);
+#endif
 
-	for (;;) {
-		uint32_t hpd = HDMI_READ(sc, AWIN_HDMI_HPD_REG);
-		bool con = !!(hpd & AWIN_HDMI_HPD_HOTPLUG_DET);
+	HDMI_WRITE(sc, AWIN_HDMI_VID_CTRL_REG, 0);
+	HDMI_WRITE(sc, AWIN_HDMI_INT_STATUS_REG, 0xffffffff);
 
-		if (sc->sc_connected == con)
-			goto next;
+	val = 0;
+	if (dblscan_p) {
+		val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_REPEATER_SEL_2X,
+				 AWIN_HDMI_VID_CTRL_REPEATER_SEL);
+	}
+	if (interlace_p) {
+		val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_OUTPUT_FMT_INTERLACE,
+				 AWIN_HDMI_VID_CTRL_OUTPUT_FMT);
+	}
+	HDMI_WRITE(sc, AWIN_HDMI_VID_CTRL_REG, val);
+
+	val = __SHIFTIN((mode->hdisplay << dblscan_p) - 1,
+			AWIN_HDMI_VID_TIMING_0_ACT_H);
+	if (interlace_p) {
+		val |= __SHIFTIN((mode->vdisplay / 2) - 1,
+				 AWIN_HDMI_VID_TIMING_0_ACT_V);
+	} else {
+		val |= __SHIFTIN(mode->vdisplay - 1,
+				 AWIN_HDMI_VID_TIMING_0_ACT_V);
+	}
+	HDMI_WRITE(sc, AWIN_HDMI_VID_TIMING_0_REG, val);
 
-		sc->sc_connected = con;
-		if (con) {
-			device_printf(sc->sc_dev, "display connected\n");
-			awin_hdmi_connect(sc);
-			awin_hdmi_read_edid(sc);
-		} else {
-			device_printf(sc->sc_dev, "display disconnected\n");
-		}
+	val = __SHIFTIN((hbp << dblscan_p) - 1,
+			AWIN_HDMI_VID_TIMING_1_HBP);
+	val |= __SHIFTIN(vbp - 1,
+			 AWIN_HDMI_VID_TIMING_1_VBP);
+	HDMI_WRITE(sc, AWIN_HDMI_VID_TIMING_1_REG, val);
+
+	val = __SHIFTIN((hfp << dblscan_p) - 1,
+			AWIN_HDMI_VID_TIMING_2_HFP);
+	val |= __SHIFTIN(vfp - 1,
+			 AWIN_HDMI_VID_TIMING_2_VFP);
+	HDMI_WRITE(sc, AWIN_HDMI_VID_TIMING_2_REG, val);
+
+	val = __SHIFTIN((hspw << dblscan_p) - 1,
+			AWIN_HDMI_VID_TIMING_3_HSPW);
+	val |= __SHIFTIN(vspw - 1,
+			 AWIN_HDMI_VID_TIMING_3_VSPW);
+	HDMI_WRITE(sc, AWIN_HDMI_VID_TIMING_3_REG, val);
+
+	val = 0;
+	if (phsync_p) {
+		val |= AWIN_HDMI_VID_TIMING_4_HSYNC_ACTIVE_SEL;
+	}
+	if (pvsync_p) {
+		val |= AWIN_HDMI_VID_TIMING_4_VSYNC_ACTIVE_SEL;
+	}
+	val |= __SHIFTIN(AWIN_HDMI_VID_TIMING_4_TX_CLOCK_NORMAL,
+			 AWIN_HDMI_VID_TIMING_4_TX_CLOCK);
+	HDMI_WRITE(sc, AWIN_HDMI_VID_TIMING_4_REG, val);
+
+	u_int clk_div = awin_tcon_get_clk_div();
+
+#ifdef AWIN_HDMI_DEBUG
+	device_printf(sc->sc_dev, "dot_clock: %d\n", mode->dot_clock);
+	device_printf(sc->sc_dev, "clkdiv: %d\n", clk_div);
+#endif
+
+	if (clk_div == 0) {
+		device_printf(sc->sc_dev, "ERROR: TCON clk not configured\n");
+		return;
+	}
+
+	uint32_t pll_ctrl, pad_ctrl0, pad_ctrl1;
+	if (HDMI_1_4_P(sc)) {
+		pad_ctrl0 = 0x7e8000ff;
+		pad_ctrl1 = 0x01ded030 | 0x40;
+		pll_ctrl = 0xba48a308;
+		pll_ctrl |= __SHIFTIN(clk_div-1, AWIN_HDMI_PLL_CTRL_PREDIV);
+	} else {
+		pad_ctrl0 = 0xfe800000;
+		pad_ctrl1 = 0x00d8c830 | 0x40;
+		pll_ctrl = 0xfa4ef708;
+		pll_ctrl |= __SHIFTIN(clk_div, AWIN_HDMI_PLL_CTRL_PREDIV);
+	}
+
+	HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL0_REG, pad_ctrl0);
+	HDMI_WRITE(sc, AWIN_HDMI_PAD_CTRL1_REG, pad_ctrl1);
+	HDMI_WRITE(sc, AWIN_HDMI_PLL_CTRL_REG, pll_ctrl);
+
+#if AWIN_HDMI_PLL == 7
+	HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (1<<21));
+#elif AWIN_HDMI_PLL == 3
+	HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (0<<21));
+#endif
+
+	delay(50000);
+
+	val = HDMI_READ(sc, AWIN_HDMI_VID_CTRL_REG);
+#ifdef AWIN_HDMI_CBGEN
+	val |= __SHIFTIN(AWIN_HDMI_VID_CTRL_SRC_SEL_CBGEN,
+			 AWIN_HDMI_VID_CTRL_SRC_SEL);
+#endif
+	val |= AWIN_HDMI_VID_CTRL_VIDEO_EN;
+	HDMI_WRITE(sc, AWIN_HDMI_VID_CTRL_REG, val);
+
+#if defined(AWIN_HDMI_DEBUG) && defined(DDB)
+	awin_hdmi_dump_regs();
+#endif
+}
+
+static void
+awin_hdmi_set_audiomode(struct awin_hdmi_softc *sc, struct videomode *mode)
+{
+	/* TODO */
+	HDMI_WRITE(sc, AWIN_HDMI_AUD_CTRL_REG, 0);
+}
+
+static void
+awin_hdmi_hpd(struct awin_hdmi_softc *sc)
+{
+	uint32_t hpd = HDMI_READ(sc, AWIN_HDMI_HPD_REG);
+	bool con = !!(hpd & AWIN_HDMI_HPD_HOTPLUG_DET);
+
+	if (sc->sc_connected == con)
+		return;
 
-next:
+	sc->sc_connected = con;
+	if (con) {
+		device_printf(sc->sc_dev, "display connected\n");
+		awin_hdmi_read_edid(sc);
+	} else {
+		device_printf(sc->sc_dev, "display disconnected\n");
+		awin_tcon_set_videomode(NULL);
+	}
+}
+
+static void
+awin_hdmi_thread(void *priv)
+{
+	struct awin_hdmi_softc *sc = priv;
+
+	for (;;) {
+		awin_hdmi_hpd(sc);
 		kpause("hdmihotplug", false, mstohz(1000), NULL);
 	}
 }
 
+#if 0
 static int
 awin_hdmi_intr(void *priv)
 {
@@ -529,3 +670,40 @@ awin_hdmi_intr(void *priv)
 
 	return 1;
 }
+#endif
+
+#if defined(DDB)
+void
+awin_hdmi_dump_regs(void)
+{
+	static const struct {
+		const char *name;
+		uint16_t reg;
+	} regs[] = {
+		{ "CTRL", AWIN_HDMI_CTRL_REG },
+		{ "VID_CTRL", AWIN_HDMI_VID_CTRL_REG },
+		{ "VID_TIMING_0", AWIN_HDMI_VID_TIMING_0_REG },
+		{ "VID_TIMING_1", AWIN_HDMI_VID_TIMING_1_REG },
+		{ "VID_TIMING_2", AWIN_HDMI_VID_TIMING_2_REG },
+		{ "VID_TIMING_3", AWIN_HDMI_VID_TIMING_3_REG },
+		{ "VID_TIMING_4", AWIN_HDMI_VID_TIMING_4_REG },
+		{ "PAD_CTRL0", AWIN_HDMI_PAD_CTRL0_REG },
+		{ "PAD_CTRL1", AWIN_HDMI_PAD_CTRL1_REG },
+		{ "PLL_CTRL", AWIN_HDMI_PLL_CTRL_REG },
+		{ "PLL_DBG0", AWIN_HDMI_PLL_DBG0_REG },
+		{ "PLL_DBG1", AWIN_HDMI_PLL_DBG1_REG },
+	};
+	struct awin_hdmi_softc *sc;
+	device_t dev;
+
+	dev = device_find_by_driver_unit("awinhdmi", 0);
+	if (dev == NULL)
+		return;
+	sc = device_private(dev);
+
+	for (int i = 0; i < __arraycount(regs); i++) {
+		printf("%s: 0x%08x\n", regs[i].name,
+		    HDMI_READ(sc, regs[i].reg));
+	}
+}
+#endif

Index: src/sys/arch/arm/allwinner/awin_io.c
diff -u src/sys/arch/arm/allwinner/awin_io.c:1.26 src/sys/arch/arm/allwinner/awin_io.c:1.27
--- src/sys/arch/arm/allwinner/awin_io.c:1.26	Wed Nov  5 15:05:20 2014
+++ src/sys/arch/arm/allwinner/awin_io.c	Sun Nov  9 14:10:54 2014
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.26 2014/11/05 15:05:20 jmcneill Exp $");
+__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.27 2014/11/09 14:10:54 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -108,11 +108,15 @@ static const struct awin_locators awin_l
 	{ "com", OFFANDSIZE(UART6), 6, AWIN_IRQ_UART6, A10|A20 },
 	{ "com", OFFANDSIZE(UART7), 7, AWIN_IRQ_UART7, A10|A20 },
 	{ "com", OFFANDSIZE(UART0), 0, AWIN_A31_IRQ_UART0, A31 },
+	{ "awindebe", AWIN_DE_BE0_OFFSET, 0x1000, 0, NOINTR, AANY },
+	{ "awindebe", AWIN_DE_BE1_OFFSET, 0x1000, 1, NOINTR, AANY },
+	{ "awintcon", OFFANDSIZE(LCD0), 0, NOINTR, AANY },
+	{ "awintcon", OFFANDSIZE(LCD1), 1, NOINTR, AANY },
+	{ "awinhdmi", OFFANDSIZE(HDMI), NOPORT, AWIN_IRQ_HDMI0, A20 },
+	{ "awinhdmi", OFFANDSIZE(HDMI), NOPORT, AWIN_A31_IRQ_HDMI, A31 },
 	{ "awinwdt", OFFANDSIZE(TMR), NOPORT, NOINTR, AANY },
 	{ "awinrtc", OFFANDSIZE(TMR), NOPORT, NOINTR, A10|A20 },
 	{ "awinrtc", OFFANDSIZE(A31_RTC), NOPORT, NOINTR, A31 },
-	{ "awinhdmi", OFFANDSIZE(HDMI), NOPORT, AWIN_IRQ_HDMI0, A20 },
-	{ "awinhdmi", OFFANDSIZE(HDMI), NOPORT, AWIN_A31_IRQ_HDMI, A31 },
 	{ "awinusb", OFFANDSIZE(USB1), 0, NOINTR, A10|A20 },
 	{ "awinusb", OFFANDSIZE(USB2), 1, NOINTR, A10|A20 },
 	{ "awinusb", OFFANDSIZE(A31_USB1), 0, NOINTR, A31 },

Index: src/sys/arch/arm/allwinner/awin_reg.h
diff -u src/sys/arch/arm/allwinner/awin_reg.h:1.45 src/sys/arch/arm/allwinner/awin_reg.h:1.46
--- src/sys/arch/arm/allwinner/awin_reg.h:1.45	Sat Nov  8 11:28:52 2014
+++ src/sys/arch/arm/allwinner/awin_reg.h	Sun Nov  9 14:10:54 2014
@@ -915,9 +915,9 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_USB_CLK_REG		0x00CC
 #define AWIN_SPI3_CLK_REG		0x00D4
 #define AWIN_DRAM_CLK_REG		0x0100
-#define AWIN_BE0_SCLK_CFG_REG		0x0100
-#define AWIN_BE0_SCLK_CFG_REG		0x0100
-#define AWIN_FE0_CLK_REG		0x0100
+#define AWIN_BE0_SCLK_CFG_REG		0x0104
+#define AWIN_BE1_SCLK_CFG_REG		0x0108
+#define AWIN_FE0_CLK_REG		0x010C
 #define AWIN_FE1_CLK_REG		0x0110
 #define AWIN_MP_CLK_REG			0x0114
 #define AWIN_LCD0_CH0_CLK_REG		0x0118
@@ -1111,12 +1111,22 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_GMAC_CLK_TCS_EXT_125	1
 #define AWIN_GMAC_CLK_TCS_INT_RGMII	2
 
+#define AWIN_BEx_CLK_SCLK_GATING	__BIT(31)
+#define AWIN_BEx_CLK_RST		__BIT(30)
+#define AWIN_BEx_CLK_SRC_SEL		__BITS(25,24)
+#define AWIN_BEx_CLK_SRC_SEL_PLL3	0
+#define AWIN_BEx_CLK_SRC_SEL_PLL7	1
+#define AWIN_BEx_CLK_SRC_SEL_PLL5	2
+#define AWIN_BEx_CLK_DIV_RATIO_M	__BITS(3,0)
+
 #define AWIN_LCDx_CH0_CLK_LCDx_RST	__BIT(30)
 #define AWIN_LCDx_CHx_CLK_SRC_SEL	__BITS(25,24)
 #define AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3	0
 #define AWIN_LCDx_CHx_CLK_SRC_SEL_PLL7	1
 #define AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3_2X 2
 #define AWIN_LCDx_CHx_CLK_SRC_SEL_PLL6_2 3
+#define AWIN_LCDx_CH1_SCLK1_GATING	__BIT(15)
+#define AWIN_LCDx_CH1_SCLK1_SRC_SEL	__BIT(11)
 #define AWIN_LCDx_CH1_CLK_DIV_RATIO_M	__BITS(3,0)
 
 #define AWIN_HDMI_CLK_SRC_SEL		__BITS(25,24)
@@ -1600,6 +1610,159 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_HSTMR_TMR3_CURNT_LO_REG	0x007c
 #define AWIN_HSTMR_TMR3_CURNT_HI_REG	0x0080
 
+/* DEBE */
+#define AWIN_DEBE_MODCTL_REG		0x0800
+#define AWIN_DEBE_BACKCOLOR_REG		0x0804
+#define AWIN_DEBE_DISSIZE_REG		0x0808
+#define AWIN_DEBE_LAYSIZE_REG		0x0810
+#define AWIN_DEBE_LAYCOOR_REG		0x0820
+#define AWIN_DEBE_LAYLINEWIDTH_REG	0x0840
+#define AWIN_DEBE_LAYFB_L32ADD_REG	0x0850
+#define AWIN_DEBE_LAYFB_H4ADD_REG	0x0860
+#define AWIN_DEBE_REGBUFFCTL_REG	0x0870
+#define AWIN_DEBE_CKMAX_REG		0x0880
+#define AWIN_DEBE_CKMIN_REG		0x0884
+#define AWIN_DEBE_CKCFG_REG		0x0888
+#define AWIN_DEBE_ATTCTL0_REG		0x0890
+#define AWIN_DEBE_ATTCTL1_REG		0x08A0
+#define AWIN_DEBE_HWCCTL_REG		0x08D8
+#define AWIN_DEBE_HWCFBCTL_REG		0x08E0
+#define AWIN_DEBE_WBCTL_REG		0x08F0
+#define AWIN_DEBE_WBADD_REG		0x08F4
+#define AWIN_DEBE_WBLINEWIDTH_REG	0x08F8
+#define AWIN_DEBE_IYUVCTL_REG		0x0920
+#define AWIN_DEBE_IYUVADD_REG		0x0930
+#define AWIN_DEBE_IYUVLINEWIDTH_REG	0x0940
+#define AWIN_DEBE_YGCOEF_REG		0x0950
+#define AWIN_DEBE_YGCONS_REG		0x095C
+#define AWIN_DEBE_URCOEF_REG		0x0960
+#define AWIN_DEBE_URCONS_REG		0x096C
+#define AWIN_DEBE_VBCOEF_REG		0x0970
+#define AWIN_DEBE_VBCONS_REG		0x097C
+#define AWIN_DEBE_OCCTL_REG		0x09C0
+#define AWIN_DEBE_OCRCOEF_REG		0x09D0
+#define AWIN_DEBE_OCRCONS_REG		0x09DC
+#define AWIN_DEBE_OCGCOEF_REG		0x09E0
+#define AWIN_DEBE_OCGCONS_REG		0x09EC
+#define AWIN_DEBE_OCBCOEF_REG		0x09F0
+#define AWIN_DEBE_OCBCONS_REG		0x09FC
+
+#define AWIN_DEBE_MODCTL_LINE_SEL	__BIT(29)
+#define AWIN_DEBE_MODCTL_ITLMOD_EN	__BIT(28)
+#define AWIN_DEBE_MODCTL_OUT_SEL	__BITS(22,20)
+#define AWIN_DEBE_MODCTL_OUT_SEL_LCD	0
+#define AWIN_DEBE_MODCTL_OUT_SEL_FE0	6
+#define AWIN_DEBE_MODCTL_OUT_SEL_FE1	7
+#define AWIN_DEBE_MODCTL_OSCA_EN	__BIT(17)
+#define AWIN_DEBE_MODCTL_HWC_EN		__BIT(16)
+#define AWIN_DEBE_MODCTL_LAY3_EN	__BIT(11)
+#define AWIN_DEBE_MODCTL_LAY2_EN	__BIT(10)
+#define AWIN_DEBE_MODCTL_LAY1_EN	__BIT(9)
+#define AWIN_DEBE_MODCTL_LAY0_EN	__BIT(8)
+#define AWIN_DEBE_MODCTL_START_CTL	__BIT(1)
+#define AWIN_DEBE_MODCTL_EN		__BIT(0)
+
+#define AWIN_DEBE_ATTCTL0_LAY_GLBALPHA	__BITS(31,24)
+#define AWIN_DEBE_ATTCTL0_LAY_WORKMOD	__BITS(23,22)
+#define AWIN_DEBE_ATTCTL0_PREMUL	__BIT(21,20)
+#define AWIN_DEBE_ATTCTL0_CKEN		__BITS(19,18)
+#define AWIN_DEBE_ATTCTL0_LAY_PIPESEL	__BIT(15)
+#define AWIN_DEBE_ATTCTL0_LAY_PRISEL	__BITS(11,10)
+#define AWIN_DEBE_ATTCTL0_LAY_VDOSEL	__BIT(4)
+#define AWIN_DEBE_ATTCTL0_LAY_YUVEN	__BIT(2)
+#define AWIN_DEBE_ATTCTL0_LAY_VDOEN	__BIT(1)
+#define AWIN_DEBE_ATTCTL0_LAY_GLBALPHAEN __BIT(0)
+
+#define AWIN_DEBE_ATTCTL1_LAY_HSCAFCT	__BITS(15,14)
+#define AWIN_DEBE_ATTCTL1_LAY_WSCAFCT	__BITS(13,12)
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT	__BITS(11,8)
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_MONO_1BPP	0
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_MONO_2BPP	1
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_MONO_4BPP	2
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_MONO_8BPP	3
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_RGB655	4
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_RGB565	5
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_RGB556	6
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_ARGB1555	7
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_RGBA5551	8
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_XRGB8888	9
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_ARGB8888	10
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_RGB888	11
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_ARGB4444	12
+#define AWIN_DEBE_ATTCTL1_LAY_FBFMT_RGBA4444	13
+#define AWIN_DEBE_ATTCTL1_LAY_BRSWAPEN	__BIT(2)
+#define AWIN_DEBE_ATTCTL1_LAY_FBPS	__BITS(1,0)
+
+#define AWIN_DEBE_REGBUFFCTL_REGAUTOLOAD_DIS __BIT(1)
+#define AWIN_DEBE_REGBUFFCTL_REGLOADCTL	__BIT(0)
+
+/* TCON */
+#define AWIN_TCON_GCTL_REG		0x0000
+#define AWIN_TCON_GINT0_REG		0x0004
+#define AWIN_TCON_GINT1_REG		0x0008
+#define AWIN_TCON0_FRM_CTL_REG		0x0010
+#define AWIN_TCON0_CTL_REG		0x0040
+#define AWIN_TCON0_DCLK_REG		0x0044
+#define AWIN_TCON0_BASIC0_REG		0x0048
+#define AWIN_TCON0_BASIC1_REG		0x004C
+#define AWIN_TCON0_BASIC2_REG		0x0050
+#define AWIN_TCON0_BASIC3_REG		0x0054
+#define AWIN_TCON0_HV_IF_REG		0x0058
+#define AWIN_TCON0_CPU_IF_REG		0x0060
+#define AWIN_TCON0_CPU_WR_REG		0x0064
+#define AWIN_TCON0_CPU_RD0_REG		0x0068
+#define AWIN_TCON0_CPU_RD1_REG		0x006C
+#define AWIN_TCON0_LVDS_IF_REG		0x0084
+#define AWIN_TCON0_IO_POL_REG		0x0088
+#define AWIN_TCON0_IO_TRI_REG		0x008C
+#define AWIN_TCON1_CTL_REG		0x0090
+#define AWIN_TCON1_BASIC0_REG		0x0094
+#define AWIN_TCON1_BASIC1_REG		0x0098
+#define AWIN_TCON1_BASIC2_REG		0x009C
+#define AWIN_TCON1_BASIC3_REG		0x00A0
+#define AWIN_TCON1_BASIC4_REG		0x00A4
+#define AWIN_TCON1_BASIC5_REG		0x00A8
+#define AWIN_TCON1_IO_POL_REG		0x00F0
+#define AWIN_TCON1_IO_TRI_REG		0x00F4
+#define AWIN_TCON_CEU_CTL_REG		0x0100
+#define AWIN_TCON0_CPU_TRI0_REG		0x0160
+#define AWIN_TCON0_CPU_TRI1_REG		0x0164
+#define AWIN_TCON0_CPU_TRI2_REG		0x0168
+#define AWIN_TCON0_CPU_TRI3_REG		0x016C
+#define AWIN_TCON_CMAP_CTL_REG		0x0180
+#define AWIN_TCON_CMAP_ODD0_REG		0x0190
+#define AWIN_TCON_CMAP_ODD1_REG		0x0194
+#define AWIN_TCON_CMAP_EVEN0_REG	0x0198
+#define AWIN_TCON_CMAP_EVEN1_REG	0x019C
+#define AWIN_TCON_MUX_CTL_REG		0x0200
+
+#define AWIN_TCON_GCTL_EN		__BIT(31)
+#define AWIN_TCON_GCTL_GAMMA_EN		__BIT(30)
+#define AWIN_TCON_GCTL_IO_MAP_SEL	__BIT(0)
+#define AWIN_TCON_GCTL_IO_MAP_SEL_TCON0	0
+#define AWIN_TCON_GCTL_IO_MAP_SEL_TCON1	1
+
+#define AWIN_TCON_GINT1_TCON0_LINENO	__BITS(27,16)
+#define AWIN_TCON_GINT1_TCON1_LINENO	__BITS(11,0)
+
+#define AWIN_TCON_CTL_EN		__BIT(31)
+#define AWIN_TCON_CTL_START_DELAY	__BITS(8,4)
+#define AWIN_TCON_CTL_SRC_SEL		__BITS(1,0)
+#define AWIN_TCON_CTL_SRC_SEL_DE0	0
+#define AWIN_TCON_CTL_SRC_SEL_DE1	1
+#define AWIN_TCON_CTL_SRC_SEL_BLUEDATA	2
+
+#define AWIN_TCON_IO_TRI_IO3		__BIT(27)
+#define AWIN_TCON_IO_TRI_IO2		__BIT(26)
+#define AWIN_TCON_IO_TRI_IO1		__BIT(25)
+#define AWIN_TCON_IO_TRI_IO0		__BIT(24)
+#define AWIN_TCON_IO_TRI_DATA		__BITS(23,0)
+
+#define AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC __BITS(9,8)
+#define AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_LCDC0_TCON1	0
+#define AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_LCDC1_TCON1	1
+#define AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_CLOSE		2
+
 /* HDMI */
 #define AWIN_HDMI_VERSION_ID_REG	0x0000
 #define AWIN_HDMI_CTRL_REG		0x0004
@@ -1817,6 +1980,7 @@ struct awin_mmc_idma_descriptor {
 
 #define AWIN_A31_CPU_AXI_CFG_REG		0x0050
 
+#define AWIN_A31_PLL10_CFG_REG		0x0048
 #define AWIN_A31_GMAC_CLK_REG		0x00D0
 #define AWIN_A31_AHB_RESET0_REG		0x02C0
 #define AWIN_A31_AHB_RESET1_REG		0x02C4
@@ -1836,6 +2000,14 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_A31_PLL2_CFG_FACTOR_N	__BITS(14,8)
 #define AWIN_A31_PLL2_CFG_PREVDIV_M	__BITS(4,0)
 
+#define AWIN_A31_PLL3_CFG_MODE		__BIT(30)
+#define AWIN_A31_PLL3_CFG_LOCK		__BIT(28)
+#define AWIN_A31_PLL3_CFG_FRAC_CLK_OUT	__BIT(25)
+#define AWIN_A31_PLL3_CFG_MODE_SEL	__BIT(24)
+#define AWIN_A31_PLL3_CFG_SDM_EN	__BIT(20)
+#define AWIN_A31_PLL3_CFG_FACTOR_N	__BITS(14,8)
+#define AWIN_A31_PLL3_CFG_PREDIV_M	__BITS(3,0)
+
 #define AWIN_A31_PLL7_CFG_MODE		__BIT(30)
 #define AWIN_A31_PLL7_CFG_LOCK		__BIT(28)
 #define AWIN_A31_PLL7_CFG_FRAC_CLK_OUT	__BIT(25)
@@ -1844,6 +2016,16 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_A31_PLL7_CFG_FACTOR_N	__BITS(14,8)
 #define AWIN_A31_PLL7_CFG_PREDIV_M	__BITS(3,0)
 
+#define AWIN_A31_PLL10_CFG_MODE		__BIT(30)
+#define AWIN_A31_PLL10_CFG_LOCK		__BIT(28)
+#define AWIN_A31_PLL10_CFG_FRAC_CLK_OUT	__BIT(25)
+#define AWIN_A31_PLL10_CFG_MODE_SEL	__BIT(24)
+#define AWIN_A31_PLL10_CFG_SDM_EN	__BIT(20)
+#define AWIN_A31_PLL10_CFG_FACTOR_N	__BITS(14,8)
+#define AWIN_A31_PLL10_CFG_PREDIV_M	__BITS(3,0)
+
+#define AWIN_A31_AHB_GATING0_USB_OHCI2	__BIT(31)
+#define AWIN_A31_AHB_GATING0_USB_OHCI1	__BIT(30)
 #define AWIN_A31_AHB_GATING0_USB_OHCI2	__BIT(31)
 #define AWIN_A31_AHB_GATING0_USB_OHCI1	__BIT(30)
 #define AWIN_A31_AHB_GATING0_USB_OHCI0	__BIT(29)
@@ -2069,6 +2251,14 @@ struct awin_a31_dma_desc {
 };
 #endif
 
+#define AWIN_A31_BEx_CLK_SRC_SEL	__BITS(26,24)
+#define AWIN_A31_BEx_CLK_SRC_SEL_PLL3		0
+#define AWIN_A31_BEx_CLK_SRC_SEL_PLL7		1
+#define AWIN_A31_BEx_CLK_SRC_SEL_PLL6_2X	2
+#define AWIN_A31_BEx_CLK_SRC_SEL_PLL8		3
+#define AWIN_A31_BEx_CLK_SRC_SEL_PLL9		4
+#define AWIN_A31_BEx_CLK_SRC_SEL_PLL10		5
+
 #define AWIN_A31_HDMI_DDC_CTRL_REG		0x0500
 #define AWIN_A31_HDMI_DDC_EXCTRL_REG		0x0504
 #define AWIN_A31_HDMI_DDC_COMMAND_REG		0x0508

Index: src/sys/arch/arm/allwinner/awin_var.h
diff -u src/sys/arch/arm/allwinner/awin_var.h:1.19 src/sys/arch/arm/allwinner/awin_var.h:1.20
--- src/sys/arch/arm/allwinner/awin_var.h:1.19	Sun Nov  2 23:54:16 2014
+++ src/sys/arch/arm/allwinner/awin_var.h	Sun Nov  9 14:10:54 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_var.h,v 1.19 2014/11/02 23:54:16 jmcneill Exp $ */
+/* $NetBSD: awin_var.h,v 1.20 2014/11/09 14:10:54 jmcneill Exp $ */
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -62,6 +62,16 @@ struct awinio_attach_args {
 	bus_dma_tag_t aio_coherent_dmat;
 };
 
+struct awinfb_attach_args {
+	void *afb_fb;
+	uint32_t afb_width;
+	uint32_t afb_height;
+	bool afb_console;
+	bus_dma_tag_t afb_dmat;
+	bus_dma_segment_t *afb_dmasegs;
+	int afb_ndmasegs;
+};
+
 struct awin_gpio_pinset {
 	uint8_t pinset_group;
 	uint8_t pinset_func;
@@ -86,8 +96,10 @@ psize_t awin_memprobe(void);
 void	awin_bootstrap(vaddr_t, vaddr_t); 
 void	awin_dma_bootstrap(psize_t);
 void	awin_pll2_enable(void);
+void	awin_pll3_enable(void);
 void	awin_pll6_enable(void);
 void	awin_pll7_enable(void);
+void	awin_pll3_set_rate(uint32_t);
 void	awin_cpu_hatch(struct cpu_info *);
 
 #define AWIN_CHIP_ID_A10	AWIN_SRAM_VER_KEY_A10
@@ -111,6 +123,13 @@ void	awin_dma_set_config(void *, uint32_
 int	awin_dma_transfer(void *, paddr_t, paddr_t, size_t);
 void	awin_dma_halt(void *);
 
+struct videomode;
+unsigned int awin_tcon_get_clk_div(void);
+void	awin_tcon_set_videomode(struct videomode *);
+void	awin_debe_set_videomode(struct videomode *);
+void	awin_fb_set_videomode(device_t, struct videomode *);
+void	awin_fb_ddb_trap_callback(int);
+
 void	awin_wdog_reset(void);
 void	awin_tmr_cpu_init(struct cpu_info *);
 

Index: src/sys/arch/arm/allwinner/files.awin
diff -u src/sys/arch/arm/allwinner/files.awin:1.21 src/sys/arch/arm/allwinner/files.awin:1.22
--- src/sys/arch/arm/allwinner/files.awin:1.21	Sat Nov  8 00:48:06 2014
+++ src/sys/arch/arm/allwinner/files.awin	Sun Nov  9 14:10:54 2014
@@ -1,4 +1,4 @@
-#	$NetBSD: files.awin,v 1.21 2014/11/08 00:48:06 jmcneill Exp $
+#	$NetBSD: files.awin,v 1.22 2014/11/09 14:10:54 jmcneill Exp $
 #
 # Configuration info for Allwinner ARM Peripherals
 #
@@ -139,6 +139,20 @@ device	awinhdmi: edid, ddc_read_edid
 attach	awinhdmi at awinio with awin_hdmi
 file	arch/arm/allwinner/awin_hdmi.c		awin_hdmi
 
+# A10/A20/A31 LCD/TV timing controller (TCON)
+device	awintcon
+attach	awintcon at awinio with awin_tcon
+file	arch/arm/allwinner/awin_tcon.c		awin_tcon | awin_hdmi
+
+# A10/A20/A31 Display engine backend (DE-BE)
+device	awindebe { }
+attach	awindebe at awinio with awin_debe
+file	arch/arm/allwinner/awin_debe.c		awin_debe | awin_hdmi
+
+# Framebuffer
+attach	genfb at awindebe with awin_fb: edid
+file	arch/arm/allwinner/awin_fb.c		awin_fb		needs-flag
+
 # A10/A20/A31 Consumer IR (CIR)
 device	awinir: irbus
 attach	awinir at awinio with awin_ir

Added files:

Index: src/sys/arch/arm/allwinner/awin_debe.c
diff -u /dev/null src/sys/arch/arm/allwinner/awin_debe.c:1.1
--- /dev/null	Sun Nov  9 14:10:54 2014
+++ src/sys/arch/arm/allwinner/awin_debe.c	Sun Nov  9 14:10:54 2014
@@ -0,0 +1,301 @@
+/* $NetBSD: awin_debe.c,v 1.1 2014/11/09 14:10:54 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2014 Jared D. McNeill <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef AWIN_DEBE_VIDEOMEM
+#define AWIN_DEBE_VIDEOMEM	(16 * 1024 * 1024)
+#endif
+
+#include "genfb.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: awin_debe.c,v 1.1 2014/11/09 14:10:54 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+
+#include <arm/allwinner/awin_reg.h>
+#include <arm/allwinner/awin_var.h>
+
+#include <dev/videomode/videomode.h>
+
+struct awin_debe_softc {
+	device_t sc_dev;
+	device_t sc_fbdev;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+	bus_space_handle_t sc_ccm_bsh;
+	bus_dma_tag_t sc_dmat;
+	unsigned int sc_port;
+
+	bus_dma_segment_t sc_dmasegs[1];
+	bus_size_t sc_dmasize;
+	bus_dmamap_t sc_dmamap;
+	void *sc_dmap;
+};
+
+#define DEBE_READ(sc, reg) \
+    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define DEBE_WRITE(sc, reg, val) \
+    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static int	awin_debe_match(device_t, cfdata_t, void *);
+static void	awin_debe_attach(device_t, device_t, void *);
+
+static int	awin_debe_alloc_videomem(struct awin_debe_softc *);
+static void	awin_debe_setup_fbdev(struct awin_debe_softc *,
+				      struct videomode *);
+
+CFATTACH_DECL_NEW(awin_debe, sizeof(struct awin_debe_softc),
+	awin_debe_match, awin_debe_attach, NULL, NULL);
+
+static int
+awin_debe_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct awinio_attach_args * const aio = aux;
+	const struct awin_locators * const loc = &aio->aio_loc;
+
+	if (strcmp(cf->cf_name, loc->loc_name))
+		return 0;
+
+	return 1;
+}
+
+static void
+awin_debe_attach(device_t parent, device_t self, void *aux)
+{
+	struct awin_debe_softc *sc = device_private(self);
+	struct awinio_attach_args * const aio = aux;
+	const struct awin_locators * const loc = &aio->aio_loc;
+	int error;
+
+	sc->sc_dev = self;
+	sc->sc_bst = aio->aio_core_bst;
+	sc->sc_dmat = aio->aio_dmat;
+	sc->sc_port = loc->loc_port;
+	bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
+	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
+	bus_space_subregion(sc->sc_bst, aio->aio_ccm_bsh, 0, 0x1000,
+	    &sc->sc_ccm_bsh);
+
+	aprint_naive("\n");
+	aprint_normal(": Display Engine Backend\n");
+
+	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_A31_AHB_RESET1_REG,
+		    AWIN_A31_AHB_RESET1_BE0_RST << loc->loc_port,
+		    0);
+	}
+
+	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_BE0_SCLK_CFG_REG + (loc->loc_port * 4),
+		    __SHIFTIN(AWIN_A31_BEx_CLK_SRC_SEL_PLL6_2X,
+			      AWIN_A31_BEx_CLK_SRC_SEL),
+		    AWIN_A31_BEx_CLK_SRC_SEL);
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_BE0_SCLK_CFG_REG + (loc->loc_port * 4),
+		    4 - 1, AWIN_BEx_CLK_DIV_RATIO_M);
+	} else {
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_BE0_SCLK_CFG_REG + (loc->loc_port * 4),
+		    AWIN_BEx_CLK_RST |
+		    __SHIFTIN(AWIN_BEx_CLK_SRC_SEL_PLL5, AWIN_BEx_CLK_SRC_SEL),
+		    AWIN_BEx_CLK_SRC_SEL);
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_BE0_SCLK_CFG_REG + (loc->loc_port * 4),
+		    0 /* XXX */, AWIN_BEx_CLK_DIV_RATIO_M);
+	}
+
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_AHB_GATING1_REG, AWIN_AHB_GATING1_DE_BE0 << loc->loc_port, 0);
+
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_DRAM_CLK_REG,
+	    AWIN_DRAM_CLK_BE0_DCLK_ENABLE << loc->loc_port, 0);
+
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_BE0_SCLK_CFG_REG + (loc->loc_port * 4),
+	    AWIN_CLK_ENABLE, 0);
+
+	for (unsigned int reg = 0x800; reg < 0x1000; reg += 4) {
+		DEBE_WRITE(sc, reg, 0);
+	}
+
+	DEBE_WRITE(sc, AWIN_DEBE_MODCTL_REG, AWIN_DEBE_MODCTL_EN);
+
+	error = awin_debe_alloc_videomem(sc);
+	if (error) {
+		aprint_error_dev(sc->sc_dev,
+		    "couldn't allocate video memory, error = %d\n", error);
+		return;
+	}
+}
+
+static int
+awin_debe_alloc_videomem(struct awin_debe_softc *sc)
+{
+	int error, nsegs;
+
+	sc->sc_dmasize = AWIN_DEBE_VIDEOMEM;
+	error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0,
+	    sc->sc_dmasize, sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
+	if (error)
+		return error;
+	error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs,
+	    sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
+	if (error)
+		goto free;
+	error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1,
+	    sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap);
+	if (error)
+		goto unmap;
+	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap,
+	    sc->sc_dmasize, NULL, BUS_DMA_WAITOK);
+	if (error)
+		goto destroy;
+
+	return 0;
+
+destroy:
+	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
+unmap:
+	bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize);
+free:
+	bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs);
+
+	sc->sc_dmasize = 0;
+	sc->sc_dmap = NULL;
+
+	return error;
+}
+
+static void
+awin_debe_setup_fbdev(struct awin_debe_softc *sc, struct videomode *mode)
+{
+	if (sc->sc_fbdev == NULL) {
+		struct awinfb_attach_args afb = {
+			.afb_fb = sc->sc_dmap,
+			.afb_width = mode->hdisplay,
+			.afb_height = mode->vdisplay,
+			.afb_console = false, /* XXX */
+			.afb_dmat = sc->sc_dmat,
+			.afb_dmasegs = sc->sc_dmasegs,
+			.afb_ndmasegs = 1
+		};
+		sc->sc_fbdev = config_found_ia(sc->sc_dev, "awindebe",
+		    &afb, NULL);
+	}
+#if NGENFB > 0
+	else {
+		awin_fb_set_videomode(sc->sc_fbdev, mode);
+	}
+#endif
+}
+
+void
+awin_debe_set_videomode(struct videomode *mode)
+{
+	struct awin_debe_softc *sc;
+	device_t dev;
+	uint32_t val;
+
+	dev = device_find_by_driver_unit("awindebe", 0);
+	if (dev == NULL) {
+		printf("DEBE: no driver found\n");
+		return;
+	}
+	sc = device_private(dev);
+
+	if (mode) {
+		uint32_t vmem = mode->vdisplay * mode->hdisplay * 4;
+
+		if (vmem > sc->sc_dmasize) {
+			device_printf(sc->sc_dev,
+			    "not enough memory for %ux%u fb (req %u have %u)\n",
+			    mode->hdisplay, mode->vdisplay,
+			    vmem, (unsigned int)sc->sc_dmasize);
+			return;
+		}
+
+		/* disable */
+		val = DEBE_READ(sc, AWIN_DEBE_MODCTL_REG);
+		val &= ~AWIN_DEBE_MODCTL_LAY0_EN;
+		DEBE_WRITE(sc, AWIN_DEBE_MODCTL_REG, val);
+
+		/* notify fb */
+		awin_debe_setup_fbdev(sc, mode);
+
+		DEBE_WRITE(sc, AWIN_DEBE_DISSIZE_REG,
+		    ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+		DEBE_WRITE(sc, AWIN_DEBE_LAYSIZE_REG,
+		    ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+		DEBE_WRITE(sc, AWIN_DEBE_LAYLINEWIDTH_REG, mode->hdisplay << 5);
+		DEBE_WRITE(sc, AWIN_DEBE_LAYFB_L32ADD_REG,
+		    sc->sc_dmamap->dm_segs[0].ds_addr << 3);
+		DEBE_WRITE(sc, AWIN_DEBE_LAYFB_H4ADD_REG,
+		    sc->sc_dmamap->dm_segs[0].ds_addr >> 29);
+
+		val = DEBE_READ(sc, AWIN_DEBE_ATTCTL1_REG);
+		val &= ~AWIN_DEBE_ATTCTL1_LAY_FBFMT;
+		val |= AWIN_DEBE_ATTCTL1_LAY_FBFMT_XRGB8888;
+		val &= ~AWIN_DEBE_ATTCTL1_LAY_BRSWAPEN;
+		val &= ~AWIN_DEBE_ATTCTL1_LAY_FBPS;
+		DEBE_WRITE(sc, AWIN_DEBE_ATTCTL1_REG, val);
+
+		val = DEBE_READ(sc, AWIN_DEBE_MODCTL_REG);
+		val |= AWIN_DEBE_MODCTL_LAY0_EN;
+		DEBE_WRITE(sc, AWIN_DEBE_MODCTL_REG, val);
+
+		val = DEBE_READ(sc, AWIN_DEBE_REGBUFFCTL_REG);
+		val |= AWIN_DEBE_REGBUFFCTL_REGLOADCTL;
+		DEBE_WRITE(sc, AWIN_DEBE_REGBUFFCTL_REG, val);
+
+		delay(50000);
+
+		/* enable */
+		val = DEBE_READ(sc, AWIN_DEBE_MODCTL_REG);
+		val |= AWIN_DEBE_MODCTL_START_CTL;
+		DEBE_WRITE(sc, AWIN_DEBE_MODCTL_REG, val);
+	} else {
+		/* disable */
+		val = DEBE_READ(sc, AWIN_DEBE_MODCTL_REG);
+		val &= ~AWIN_DEBE_MODCTL_LAY0_EN;
+		val &= ~AWIN_DEBE_MODCTL_START_CTL;
+		DEBE_WRITE(sc, AWIN_DEBE_MODCTL_REG, val);
+
+		/* notify fb */
+		awin_debe_setup_fbdev(sc, mode);
+	}
+}
Index: src/sys/arch/arm/allwinner/awin_fb.c
diff -u /dev/null src/sys/arch/arm/allwinner/awin_fb.c:1.1
--- /dev/null	Sun Nov  9 14:10:54 2014
+++ src/sys/arch/arm/allwinner/awin_fb.c	Sun Nov  9 14:10:54 2014
@@ -0,0 +1,195 @@
+/* $NetBSD: awin_fb.c,v 1.1 2014/11/09 14:10:54 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2014 Jared D. McNeill <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: awin_fb.c,v 1.1 2014/11/09 14:10:54 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+
+#include <arm/allwinner/awin_reg.h>
+#include <arm/allwinner/awin_var.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/wsfb/genfbvar.h>
+
+struct awin_fb_softc {
+	struct genfb_softc sc_gen;
+
+	bus_dma_tag_t sc_dmat;
+	bus_dma_segment_t *sc_dmasegs;
+	int sc_ndmasegs;
+};
+
+static device_t	awin_fb_consoledev = NULL;
+
+static int	awin_fb_match(device_t, cfdata_t, void *);
+static void	awin_fb_attach(device_t, device_t, void *);
+
+static int	awin_fb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
+static paddr_t	awin_fb_mmap(void *, void *, off_t, int);
+static bool	awin_fb_shutdown(device_t, int);
+
+CFATTACH_DECL_NEW(awin_fb, sizeof(struct awin_fb_softc),
+	awin_fb_match, awin_fb_attach, NULL, NULL);
+
+static int
+awin_fb_match(device_t parent, cfdata_t cf, void *aux)
+{
+	return 1;
+}
+
+static void
+awin_fb_attach(device_t parent, device_t self, void *aux)
+{
+	struct awin_fb_softc *sc = device_private(self);
+	struct awinfb_attach_args * const afb = aux;
+	prop_dictionary_t cfg = device_properties(self);
+	struct genfb_ops ops;
+
+	if (awin_fb_consoledev == NULL)
+		awin_fb_consoledev = self;
+
+	sc->sc_gen.sc_dev = self;
+	sc->sc_dmat = afb->afb_dmat;
+	sc->sc_dmasegs = afb->afb_dmasegs;
+	sc->sc_ndmasegs = afb->afb_ndmasegs;
+
+	prop_dictionary_set_bool(cfg, "is_console", afb->afb_console);
+	prop_dictionary_set_uint32(cfg, "width", afb->afb_width);
+	prop_dictionary_set_uint32(cfg, "height", afb->afb_height);
+	prop_dictionary_set_uint8(cfg, "depth", 32);
+	prop_dictionary_set_uint16(cfg, "linebytes", afb->afb_width * 4);
+	prop_dictionary_set_uint32(cfg, "address", 0);
+	prop_dictionary_set_uint32(cfg, "virtual_address",
+	    (uintptr_t)afb->afb_fb);
+
+	genfb_init(&sc->sc_gen);
+
+	if (sc->sc_gen.sc_width == 0 || sc->sc_gen.sc_fbsize == 0) {
+		aprint_normal(": disabled\n");
+		return;
+	}
+
+	pmf_device_register1(self, NULL, NULL, awin_fb_shutdown);
+
+	memset(&ops, 0, sizeof(ops));
+	ops.genfb_ioctl = awin_fb_ioctl;
+	ops.genfb_mmap = awin_fb_mmap;
+
+	aprint_naive("\n");
+
+	if (afb->afb_console)
+		aprint_normal(": switching to framebuffer console\n");
+	else
+		aprint_normal("\n");
+
+	genfb_attach(&sc->sc_gen, &ops);
+}
+
+static int
+awin_fb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
+{
+	struct awin_fb_softc *sc = v;
+	struct wsdisplayio_bus_id *busid;
+	struct wsdisplayio_fbinfo *fbi;
+	struct rasops_info *ri;
+	int error;
+
+	switch (cmd) {
+	case WSDISPLAYIO_GTYPE:
+		*(u_int *)data = WSDISPLAY_TYPE_ALLWINNER;
+		return 0;
+	case WSDISPLAYIO_GET_BUSID:
+		busid = data;
+		busid->bus_type = WSDISPLAYIO_BUS_SOC;
+		return 0;
+	case WSDISPLAYIO_GET_FBINFO:
+		fbi = data;
+		ri = &sc->sc_gen.vd.active->scr_ri;
+		error = wsdisplayio_get_fbinfo(ri, fbi);
+		if (error == 0)
+			fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
+		return error;
+	default:
+		return EPASSTHROUGH;
+	}
+}
+
+static paddr_t
+awin_fb_mmap(void *v, void *vs, off_t off, int prot)
+{
+	struct awin_fb_softc *sc = v;
+
+	if (off < 0 || off >= sc->sc_gen.sc_fbsize)
+		return -1;
+
+	return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, sc->sc_ndmasegs,
+	    off, prot, BUS_DMA_PREFETCHABLE);
+}
+
+static bool
+awin_fb_shutdown(device_t self, int flags)
+{
+	genfb_enable_polling(self);
+	return true;
+}
+
+void
+awin_fb_ddb_trap_callback(int where)
+{
+	if (awin_fb_consoledev == NULL)
+		return;
+
+	if (where) {
+		genfb_enable_polling(awin_fb_consoledev);
+	} else {
+		genfb_disable_polling(awin_fb_consoledev);
+	}
+}
+
+void
+awin_fb_set_videomode(device_t dev, struct videomode *mode)
+{
+	struct awin_fb_softc *sc = device_private(dev);
+
+	if (mode == NULL)
+		return;
+
+	if (sc->sc_gen.sc_width != mode->hdisplay ||
+	    sc->sc_gen.sc_height != mode->vdisplay) {
+		device_printf(sc->sc_gen.sc_dev,
+		    "mode switching not yet supported\n");
+	}
+}
Index: src/sys/arch/arm/allwinner/awin_tcon.c
diff -u /dev/null src/sys/arch/arm/allwinner/awin_tcon.c:1.1
--- /dev/null	Sun Nov  9 14:10:54 2014
+++ src/sys/arch/arm/allwinner/awin_tcon.c	Sun Nov  9 14:10:54 2014
@@ -0,0 +1,249 @@
+/* $NetBSD: awin_tcon.c,v 1.1 2014/11/09 14:10:54 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2014 Jared D. McNeill <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: awin_tcon.c,v 1.1 2014/11/09 14:10:54 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+
+#include <arm/allwinner/awin_reg.h>
+#include <arm/allwinner/awin_var.h>
+
+#include <dev/videomode/videomode.h>
+
+struct awin_tcon_softc {
+	device_t sc_dev;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+	bus_space_handle_t sc_ccm_bsh;
+	unsigned int sc_port;
+	unsigned int sc_clk_div;
+};
+
+#define TCON_READ(sc, reg) \
+    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define TCON_WRITE(sc, reg, val) \
+    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static int	awin_tcon_match(device_t, cfdata_t, void *);
+static void	awin_tcon_attach(device_t, device_t, void *);
+
+static void	awin_tcon_set_pll(struct awin_tcon_softc *, struct videomode *);
+
+CFATTACH_DECL_NEW(awin_tcon, sizeof(struct awin_tcon_softc),
+	awin_tcon_match, awin_tcon_attach, NULL, NULL);
+
+static int
+awin_tcon_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct awinio_attach_args * const aio = aux;
+	const struct awin_locators * const loc = &aio->aio_loc;
+
+	if (strcmp(cf->cf_name, loc->loc_name))
+		return 0;
+
+	return 1;
+}
+
+static void
+awin_tcon_attach(device_t parent, device_t self, void *aux)
+{
+	struct awin_tcon_softc *sc = device_private(self);
+	struct awinio_attach_args * const aio = aux;
+	const struct awin_locators * const loc = &aio->aio_loc;
+
+	sc->sc_dev = self;
+	sc->sc_bst = aio->aio_core_bst;
+	sc->sc_port = loc->loc_port;
+	bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
+	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
+	bus_space_subregion(sc->sc_bst, aio->aio_ccm_bsh, 0, 0x1000,
+	    &sc->sc_ccm_bsh);
+
+	aprint_naive("\n");
+	aprint_normal(": LCD/TV timing controller\n");
+
+	awin_pll3_enable();
+
+	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_A31_AHB_RESET1_REG,
+		    AWIN_A31_AHB_RESET1_LCD0_RST << loc->loc_port,
+		    0);
+	}
+
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_AHB_GATING1_REG, AWIN_AHB_GATING1_LCD0 << loc->loc_port, 0);
+
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_LCD0_CH0_CLK_REG + (loc->loc_port * 4),
+	    __SHIFTIN(AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3,
+		      AWIN_LCDx_CHx_CLK_SRC_SEL) |
+	    AWIN_CLK_OUT_ENABLE,
+	    AWIN_LCDx_CHx_CLK_SRC_SEL);
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_LCD0_CH1_CLK_REG + (loc->loc_port * 4),
+	    __SHIFTIN(AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3,
+		      AWIN_LCDx_CHx_CLK_SRC_SEL) |
+	    AWIN_CLK_OUT_ENABLE,
+	    AWIN_LCDx_CHx_CLK_SRC_SEL | AWIN_LCDx_CH1_SCLK1_SRC_SEL);
+
+	TCON_WRITE(sc, AWIN_TCON_GCTL_REG,
+	    __SHIFTIN(AWIN_TCON_GCTL_IO_MAP_SEL_TCON1,
+		      AWIN_TCON_GCTL_IO_MAP_SEL) |
+	    AWIN_TCON_GCTL_EN);
+	TCON_WRITE(sc, AWIN_TCON_GINT0_REG, 0);
+	TCON_WRITE(sc, AWIN_TCON_GINT1_REG,
+	    __SHIFTIN(0x20, AWIN_TCON_GINT1_TCON1_LINENO));
+	TCON_WRITE(sc, AWIN_TCON0_DCLK_REG, 0xf0000000);
+	TCON_WRITE(sc, AWIN_TCON0_IO_TRI_REG, 0xffffffff);
+	TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, 0xffffffff);
+}
+
+static void
+awin_tcon_set_pll(struct awin_tcon_softc *sc, struct videomode *mode)
+{
+	unsigned int n, m, freq;
+	unsigned int m1 = ~0, n1 = ~0;
+
+	for (m = 1; m <= 15; m++) {
+		n = (m * mode->dot_clock) / 3000;
+		freq = (n * 3000) / m;
+
+		if (freq == mode->dot_clock && m < m1) {
+			m1 = m;
+			n1 = n;
+		}
+	}
+
+	if (m1 == ~0) {
+		device_printf(sc->sc_dev, "couldn't set rate %u Hz\n",
+		    mode->dot_clock * 1000);
+		sc->sc_clk_div = 0;
+		return;
+	}
+
+	awin_pll3_set_rate(n1 * 3000000);
+
+	awin_reg_set_clear(sc->sc_bst, sc->sc_ccm_bsh,
+	    AWIN_LCD0_CH1_CLK_REG + (sc->sc_port * 4),
+	    AWIN_CLK_OUT_ENABLE |
+	    AWIN_LCDx_CH1_SCLK1_GATING |
+	    __SHIFTIN(m1 - 1, AWIN_LCDx_CH1_CLK_DIV_RATIO_M),
+	    AWIN_LCDx_CH1_CLK_DIV_RATIO_M);
+
+	sc->sc_clk_div = m1;
+}
+
+void
+awin_tcon_set_videomode(struct videomode *mode)
+{
+	struct awin_tcon_softc *sc;
+	device_t dev;
+	uint32_t val;
+
+	dev = device_find_by_driver_unit("awintcon", 0);
+	if (dev == NULL) {
+		printf("TCON: no driver found\n");
+		return;
+	}
+	sc = device_private(dev);
+
+	val = TCON_READ(sc, AWIN_TCON1_CTL_REG);
+	if (mode) {
+		const u_int hbp = mode->htotal - mode->hsync_start;
+		const u_int hspw = mode->hsync_end - mode->hsync_start;
+		const u_int vbp = mode->vtotal - mode->vsync_start;
+		const u_int vspw = mode->vsync_end - mode->vsync_start;
+
+		/* enable */
+		val |= AWIN_TCON_CTL_EN;
+		val &= ~AWIN_TCON_CTL_START_DELAY;
+		val |= __SHIFTIN(0x1e, AWIN_TCON_CTL_START_DELAY);
+		val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE0,
+				 AWIN_TCON_CTL_SRC_SEL);
+		TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val);
+
+		/* Source width/height */
+		TCON_WRITE(sc, AWIN_TCON1_BASIC0_REG,
+		    ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+		/* Scaler width/height */
+		TCON_WRITE(sc, AWIN_TCON1_BASIC1_REG,
+		    ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+		/* Output width/height */
+		TCON_WRITE(sc, AWIN_TCON1_BASIC2_REG,
+		    ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+		/* Horizontal total + back porch */
+		TCON_WRITE(sc, AWIN_TCON1_BASIC3_REG,
+		    ((mode->htotal - 1) << 16) | (hbp - 1));
+		/* Vertical total + back porch */
+		TCON_WRITE(sc, AWIN_TCON1_BASIC4_REG,
+		    ((mode->vtotal * 2) << 16) | (vbp - 1));
+		/* Sync */
+		TCON_WRITE(sc, AWIN_TCON1_BASIC5_REG,
+		    ((hspw - 1) << 16) | (vspw - 1));
+
+		/* Setup LCDx CH1 PLL */
+		awin_tcon_set_pll(sc, mode);
+
+		val = TCON_READ(sc, AWIN_TCON_GCTL_REG);
+		val |= AWIN_TCON_GCTL_EN;
+		TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val);
+
+		val = TCON_READ(sc, AWIN_TCON1_IO_TRI_REG);
+		val &= ~0x03000000;
+		TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, val);
+	} else {
+		/* disable */
+		val &= ~AWIN_TCON_CTL_EN;
+		TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val);
+	}
+}
+
+unsigned int
+awin_tcon_get_clk_div(void)
+{
+	struct awin_tcon_softc *sc;
+	device_t dev;
+
+	dev = device_find_by_driver_unit("awintcon", 0);
+	if (dev == NULL) {
+		printf("TCON: no driver found\n");
+		return 0;
+	}
+	sc = device_private(dev);
+
+	return sc->sc_clk_div;
+}

Reply via email to