Module Name:    src
Committed By:   bouyer
Date:           Sun Oct 25 20:54:19 UTC 2015

Modified Files:
        src/sys/arch/arm/allwinner: awin_board.c awin_debe.c awin_hdmi.c
            awin_reg.h awin_tcon.c awin_var.h
        src/sys/arch/evbarm/awin: awin_machdep.c

Log Message:
Snapshot of work in progress on support for multiple display outputs.
The display configuration comes from the fex script as defined
in http://linux-sunxi.org/Fex_Guide, section disp_init.
There is some code to convert lcd0_para/lcd1_para to properties but
it's not used yet.

At this time only mode 0 (debe0->tcon0->hdmi) works.
debe0->tcon1->hdmi and debe1->tcon0->hdmi both gives a valid HDMI
signal but completely blank screen. AWIN_TCON1_BLUEDATA gives a blue screen
in both cases so tcon1->hdmi works. I suspect that, for some reason
setups other than debe0->tcon0 are not configured properly, and
the tcon is reading all-1 bits instead of the expected debe output.


To generate a diff of this commit:
cvs rdiff -u -r1.39 -r1.40 src/sys/arch/arm/allwinner/awin_board.c
cvs rdiff -u -r1.16 -r1.17 src/sys/arch/arm/allwinner/awin_debe.c \
    src/sys/arch/arm/allwinner/awin_hdmi.c
cvs rdiff -u -r1.80 -r1.81 src/sys/arch/arm/allwinner/awin_reg.h
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/allwinner/awin_tcon.c
cvs rdiff -u -r1.36 -r1.37 src/sys/arch/arm/allwinner/awin_var.h
cvs rdiff -u -r1.43 -r1.44 src/sys/arch/evbarm/awin/awin_machdep.c

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.39 src/sys/arch/arm/allwinner/awin_board.c:1.40
--- src/sys/arch/arm/allwinner/awin_board.c:1.39	Sat Oct 17 15:30:14 2015
+++ src/sys/arch/arm/allwinner/awin_board.c	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: awin_board.c,v 1.39 2015/10/17 15:30:14 bouyer Exp $	*/
+/*	$NetBSD: awin_board.c,v 1.40 2015/10/25 20:54:19 bouyer 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.39 2015/10/17 15:30:14 bouyer Exp $");
+__KERNEL_RCSID(1, "$NetBSD: awin_board.c,v 1.40 2015/10/25 20:54:19 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -920,6 +920,43 @@ awin_pll3_set_rate(uint32_t rate)
 	}
 }
 
+void
+awin_pll7_set_rate(uint32_t rate)
+{
+	const uint32_t ocfg = CCM_READ4(AWIN_PLL7_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 = 8;
+			unsigned int n = rate / (AWIN_REF_FREQ / m);
+			ncfg |= AWIN_A31_PLL7_CFG_MODE_SEL;
+			ncfg &= ~AWIN_A31_PLL7_CFG_FACTOR_N;
+			ncfg |= __SHIFTIN(n - 1, AWIN_A31_PLL7_CFG_FACTOR_N);
+			ncfg &= ~AWIN_A31_PLL7_CFG_PREDIV_M;
+			ncfg |= __SHIFTIN(m - 1, AWIN_A31_PLL7_CFG_PREDIV_M);
+		} else {
+			unsigned int m = rate / 3000000;
+			ncfg |= AWIN_PLL7_MODE_SEL;
+			ncfg &= ~AWIN_PLL7_FACTOR_M;
+			ncfg |= __SHIFTIN(m, AWIN_PLL7_FACTOR_M);
+		}
+		ncfg |= AWIN_PLL_CFG_ENABLE;
+	}
+
+	if (ncfg != ocfg) {
+		CCM_WRITE4(AWIN_PLL7_CFG_REG, ncfg);
+
+		if (awin_chip_id() == AWIN_CHIP_ID_A31) {
+			do {
+				ncfg = CCM_READ4(AWIN_PLL7_CFG_REG);
+			} while ((ncfg & AWIN_A31_PLL7_CFG_LOCK) == 0);
+		}
+	}
+}
+
 uint32_t
 awin_pll5x_get_rate(void)
 {

Index: src/sys/arch/arm/allwinner/awin_debe.c
diff -u src/sys/arch/arm/allwinner/awin_debe.c:1.16 src/sys/arch/arm/allwinner/awin_debe.c:1.17
--- src/sys/arch/arm/allwinner/awin_debe.c:1.16	Fri Oct  9 07:23:33 2015
+++ src/sys/arch/arm/allwinner/awin_debe.c	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_debe.c,v 1.16 2015/10/09 07:23:33 bouyer Exp $ */
+/* $NetBSD: awin_debe.c,v 1.17 2015/10/25 20:54:19 bouyer Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -37,7 +37,7 @@
 #define AWIN_DEBE_CURMAX	64
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_debe.c,v 1.16 2015/10/09 07:23:33 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_debe.c,v 1.17 2015/10/25 20:54:19 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -249,8 +249,8 @@ awin_debe_attach(device_t parent, device
 #endif
 
 #ifdef AWIN_DEBE_FWINIT
-	awin_debe_set_videomode(&mode);
-	awin_debe_enable(true);
+	awin_debe_set_videomode(device_unit(self), &mode);
+	awin_debe_enable(device_unit(self), true);
 #endif
 }
 
@@ -443,13 +443,13 @@ awin_debe_set_cursor(struct awin_debe_so
 }
 
 void
-awin_debe_enable(bool enable)
+awin_debe_enable(int unit, bool enable)
 {
 	struct awin_debe_softc *sc;
 	device_t dev;
 	uint32_t val;
 
-	dev = device_find_by_driver_unit("awindebe", 0);
+	dev = device_find_by_driver_unit("awindebe", unit);
 	if (dev == NULL) {
 		printf("DEBE: no driver found\n");
 		return;
@@ -472,13 +472,13 @@ awin_debe_enable(bool enable)
 }
 
 void
-awin_debe_set_videomode(const struct videomode *mode)
+awin_debe_set_videomode(int unit, const struct videomode *mode)
 {
 	struct awin_debe_softc *sc;
 	device_t dev;
 	uint32_t val;
 
-	dev = device_find_by_driver_unit("awindebe", 0);
+	dev = device_find_by_driver_unit("awindebe", unit);
 	if (dev == NULL) {
 		printf("DEBE: no driver found\n");
 		return;
Index: src/sys/arch/arm/allwinner/awin_hdmi.c
diff -u src/sys/arch/arm/allwinner/awin_hdmi.c:1.16 src/sys/arch/arm/allwinner/awin_hdmi.c:1.17
--- src/sys/arch/arm/allwinner/awin_hdmi.c:1.16	Sat Jul 25 15:19:54 2015
+++ src/sys/arch/arm/allwinner/awin_hdmi.c	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_hdmi.c,v 1.16 2015/07/25 15:19:54 jmcneill Exp $ */
+/* $NetBSD: awin_hdmi.c,v 1.17 2015/10/25 20:54:19 bouyer Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -29,10 +29,8 @@
 #include "opt_allwinner.h"
 #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.16 2015/07/25 15:19:54 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.17 2015/10/25 20:54:19 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -72,6 +70,9 @@ struct awin_hdmi_softc {
 #define DISPLAY_MODE_AUTO	0
 #define DISPLAY_MODE_HDMI	1
 #define DISPLAY_MODE_DVI	2
+	
+	int   sc_tcon_unit;
+	unsigned int sc_tcon_pll;
 
 	uint32_t sc_ver;
 	unsigned int sc_i2c_blklen;
@@ -141,30 +142,39 @@ awin_hdmi_attach(device_t parent, device
 	const struct awin_locators * const loc = &aio->aio_loc;
 	prop_dictionary_t cfg = device_properties(self);
 	uint32_t ver, clk;
+	int8_t	tcon_unit = -1;
 
 	sc->sc_dev = self;
 	sc->sc_bst = aio->aio_core_bst;
 	bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
 	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
 
-#if AWIN_HDMI_PLL == 3
-	awin_pll3_enable();
-#elif AWIN_HDMI_PLL == 7
-	awin_pll7_enable();
-#else
-#error AWIN_HDMI_PLL must be 3 or 7
-#endif
+	if (prop_dictionary_get_int8(cfg, "tcon_unit", &tcon_unit)) {
+		sc->sc_tcon_unit = tcon_unit;
+	} else {
+		sc->sc_tcon_unit = 0; /* default value */
+	}
+	sc->sc_tcon_pll = awin_tcon_get_clk_pll(sc->sc_tcon_unit);
+	switch (sc->sc_tcon_pll) {
+	case 3:
+		awin_pll3_enable();
+		clk =
+		   __SHIFTIN(AWIN_HDMI_CLK_SRC_SEL_PLL3, AWIN_HDMI_CLK_SRC_SEL);
+		break;
+	case 7:
+		awin_pll7_enable();
+		clk =
+		   __SHIFTIN(AWIN_HDMI_CLK_SRC_SEL_PLL7, AWIN_HDMI_CLK_SRC_SEL);
+		break;
+	default:
+		panic("awin_hdmi pll");
+	}
 
 	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_HDMI_RST, 0);
 	}
 
-#if AWIN_HDMI_PLL == 3
-	clk = __SHIFTIN(AWIN_HDMI_CLK_SRC_SEL_PLL3, AWIN_HDMI_CLK_SRC_SEL);
-#else
-	clk = __SHIFTIN(AWIN_HDMI_CLK_SRC_SEL_PLL7, AWIN_HDMI_CLK_SRC_SEL);
-#endif
 	clk |= AWIN_CLK_ENABLE;
 	if (awin_chip_id() == AWIN_CHIP_ID_A31) {
 		clk |= AWIN_A31_HDMI_CLK_DDC_GATING;
@@ -181,6 +191,10 @@ awin_hdmi_attach(device_t parent, device
 
 	aprint_naive("\n");
 	aprint_normal(": HDMI %d.%d\n", vmaj, vmin);
+	if (tcon_unit >= 0) {
+		aprint_verbose_dev(self, ": using TCON%d, pll%d\n",
+		    sc->sc_tcon_unit, sc->sc_tcon_pll);
+	}
 
 	sc->sc_ver = ver;
 	sc->sc_i2c_blklen = 16;
@@ -479,12 +493,16 @@ awin_hdmi_enable(struct awin_hdmi_softc 
 		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
-
+	switch(sc->sc_tcon_pll) {
+	case 3:
+		HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (0<<21));
+		break;
+	case 7:
+		HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (1<<21));
+		break;
+	default:
+		panic("awin_hdmi pll");
+	}
 	delay(1000);
 }
 
@@ -562,16 +580,13 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 
 	if (mode != NULL) {
 		awin_hdmi_video_enable(sc, false);
-		awin_tcon_enable(false);
+		awin_tcon_enable(sc->sc_tcon_unit, false);
 		delay(20000);
 
-		awin_debe_set_videomode(mode);
-		awin_tcon_set_videomode(mode);
+		awin_tcon_set_videomode(sc->sc_tcon_unit, 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);
+		awin_tcon_enable(sc->sc_tcon_unit, true);
 		delay(20000);
 		awin_hdmi_video_enable(sc, true);
 	}
@@ -715,8 +730,8 @@ awin_hdmi_set_videomode(struct awin_hdmi
 
 	HDMI_WRITE(sc, AWIN_HDMI_INT_STATUS_REG, 0xffffffff);
 
-	u_int clk_div = awin_tcon_get_clk_div();
-	bool clk_dbl = awin_tcon_get_clk_dbl();
+	u_int clk_div = awin_tcon_get_clk_div(sc->sc_tcon_unit);
+	bool clk_dbl = awin_tcon_get_clk_dbl(sc->sc_tcon_unit);
 
 #ifdef AWIN_HDMI_DEBUG
 	device_printf(sc->sc_dev, "dot_clock: %d\n", mode->dot_clock);
@@ -747,11 +762,16 @@ awin_hdmi_set_videomode(struct awin_hdmi
 	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
+	switch(sc->sc_tcon_pll) {
+	case 3:
+		HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (0<<21));
+		break;
+	case 7:
+		HDMI_WRITE(sc, AWIN_HDMI_PLL_DBG0_REG, (1<<21));
+		break;
+	default:
+		panic("awin_hdmi pll");
+	}
 
 	val = HDMI_READ(sc, AWIN_HDMI_VID_CTRL_REG);
 	val &= ~AWIN_HDMI_VID_CTRL_HDMI_MODE;
@@ -908,7 +928,7 @@ awin_hdmi_hpd(struct awin_hdmi_softc *sc
 		awin_hdmi_read_edid(sc);
 	} else {
 		device_printf(sc->sc_dev, "display disconnected\n");
-		awin_tcon_set_videomode(NULL);
+		awin_tcon_set_videomode(sc->sc_tcon_unit, NULL);
 	}
 
 	sc->sc_connected = con;

Index: src/sys/arch/arm/allwinner/awin_reg.h
diff -u src/sys/arch/arm/allwinner/awin_reg.h:1.80 src/sys/arch/arm/allwinner/awin_reg.h:1.81
--- src/sys/arch/arm/allwinner/awin_reg.h:1.80	Sat Oct 17 15:30:14 2015
+++ src/sys/arch/arm/allwinner/awin_reg.h	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_reg.h,v 1.80 2015/10/17 15:30:14 bouyer Exp $ */
+/* $NetBSD: awin_reg.h,v 1.81 2015/10/25 20:54:19 bouyer Exp $ */
 
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -1161,7 +1161,7 @@ struct awin_mmc_idma_descriptor {
 #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_CHx_CLK_SRC_SEL_PLL7_2X 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)
@@ -1804,7 +1804,7 @@ struct awin_mmc_idma_descriptor {
 #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_MUX_CTL_REG		0x0200 /* only in TCON0 */
 
 #define AWIN_TCON_GCTL_EN		__BIT(31)
 #define AWIN_TCON_GCTL_GAMMA_EN		__BIT(30)

Index: src/sys/arch/arm/allwinner/awin_tcon.c
diff -u src/sys/arch/arm/allwinner/awin_tcon.c:1.5 src/sys/arch/arm/allwinner/awin_tcon.c:1.6
--- src/sys/arch/arm/allwinner/awin_tcon.c:1.5	Fri Nov 14 00:31:54 2014
+++ src/sys/arch/arm/allwinner/awin_tcon.c	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_tcon.c,v 1.5 2014/11/14 00:31:54 jmcneill Exp $ */
+/* $NetBSD: awin_tcon.c,v 1.6 2015/10/25 20:54:19 bouyer Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -29,7 +29,7 @@
 #include "opt_allwinner.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_tcon.c,v 1.5 2014/11/14 00:31:54 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_tcon.c,v 1.6 2015/10/25 20:54:19 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -53,10 +53,15 @@ struct awin_tcon_softc {
 	bus_space_handle_t sc_bsh;
 	bus_space_handle_t sc_ch1clk_bsh;
 	unsigned int sc_port;
+	unsigned int sc_clk_pll;
 	unsigned int sc_clk_div;
 	bool sc_clk_dbl;
+	unsigned int sc_debe_unit;
 };
 
+static bus_space_handle_t tcon_mux_bsh;
+static bool tcon_mux_inited = false;
+
 #define TCON_READ(sc, reg) \
     bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
 #define TCON_WRITE(sc, reg, val) \
@@ -68,6 +73,25 @@ static void	awin_tcon_attach(device_t, d
 static void	awin_tcon_set_pll(struct awin_tcon_softc *,
 				  const struct videomode *);
 
+static void
+awin_tcon_clear_reset(struct awinio_attach_args * const aio, int unit)
+{
+	KASSERT(unit == 0 || unit == 1);
+	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 << unit,
+		    0);
+	} else {
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_LCD0_CH0_CLK_REG + (unit * 4),
+		    AWIN_LCDx_CH0_CLK_LCDx_RST, 0);
+	}
+
+	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+	    AWIN_AHB_GATING1_REG, AWIN_AHB_GATING1_LCD0 << unit, 0);
+}
+
 CFATTACH_DECL_NEW(awin_tcon, sizeof(struct awin_tcon_softc),
 	awin_tcon_match, awin_tcon_attach, NULL, NULL);
 
@@ -89,6 +113,8 @@ awin_tcon_attach(device_t parent, device
 	struct awin_tcon_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);
+	int8_t debe_unit = -1;
 
 	sc->sc_dev = self;
 	sc->sc_bst = aio->aio_core_bst;
@@ -97,26 +123,44 @@ awin_tcon_attach(device_t parent, device
 	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
 	bus_space_subregion(sc->sc_bst, aio->aio_ccm_bsh,
 	    AWIN_LCD0_CH1_CLK_REG + (loc->loc_port * 4), 4, &sc->sc_ch1clk_bsh);
+	if (!tcon_mux_inited) {
+		/* the mux register is only in LCD0 */
+		bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
+		    AWIN_LCD0_OFFSET + AWIN_TCON_MUX_CTL_REG, 4, &tcon_mux_bsh);
+		tcon_mux_inited = true;
+		/* always enable tcon0, the mux is there */
+		awin_tcon_clear_reset(aio, 0);
+	}
 
-	aprint_naive("\n");
-	aprint_normal(": LCD/TV timing controller (TCON%d)\n", loc->loc_port);
-
-	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);
+	if (prop_dictionary_get_int8(cfg, "debe_unit", &debe_unit)) {
+		sc->sc_debe_unit = debe_unit;
 	} else {
-		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
-		    AWIN_LCD0_CH0_CLK_REG + (loc->loc_port * 4),
-		    AWIN_LCDx_CH0_CLK_LCDx_RST, 0);
+		sc->sc_debe_unit = 0; /* default value */
 	}
 
-	awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
-	    AWIN_AHB_GATING1_REG, AWIN_AHB_GATING1_LCD0 << loc->loc_port, 0);
+		
+
+	aprint_naive("\n");
+	aprint_normal(": LCD/TV timing controller (TCON%d)\n", loc->loc_port);
+	switch (sc->sc_port) {
+	case 0:
+		awin_pll3_enable();
+		sc->sc_clk_pll = 3;
+		break;
+	case 1:
+		awin_pll7_enable();
+		sc->sc_clk_pll = 7;
+		break;
+	default:
+		panic("awin_tcon port\n");
+	}
+	if (debe_unit >= 0) {
+		aprint_verbose_dev(self, ": using DEBE%d, pll%d\n",
+		    sc->sc_debe_unit, sc->sc_clk_pll);
+	}
 
+	awin_tcon_clear_reset(aio, sc->sc_port);
+	
 	TCON_WRITE(sc, AWIN_TCON_GCTL_REG, 0);
 	TCON_WRITE(sc, AWIN_TCON_GINT0_REG, 0);
 	TCON_WRITE(sc, AWIN_TCON_GINT1_REG,
@@ -173,37 +217,57 @@ awin_tcon_set_pll(struct awin_tcon_softc
 	    dbl ? 'Y' : 'N', n * 3000000);
 #endif
 
-	awin_pll3_set_rate(n * 3000000);
-
-	awin_reg_set_clear(sc->sc_bst, sc->sc_ch1clk_bsh, 0,
-	    AWIN_CLK_OUT_ENABLE |
-	    AWIN_LCDx_CH1_SCLK1_GATING |
-	    __SHIFTIN(dbl ? AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3_2X :
-			    AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3,
-		      AWIN_LCDx_CHx_CLK_SRC_SEL) |
-	    __SHIFTIN(m - 1, AWIN_LCDx_CH1_CLK_DIV_RATIO_M),
-	    AWIN_LCDx_CH1_CLK_DIV_RATIO_M |
-	    AWIN_LCDx_CHx_CLK_SRC_SEL |
-	    AWIN_LCDx_CH1_SCLK1_SRC_SEL);
+	switch(sc->sc_clk_pll) {
+	case 3:
+		awin_pll3_set_rate(n * 3000000);
+		awin_reg_set_clear(sc->sc_bst, sc->sc_ch1clk_bsh, 0,
+		    AWIN_CLK_OUT_ENABLE |
+		    AWIN_LCDx_CH1_SCLK1_GATING |
+		    __SHIFTIN(dbl ? AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3_2X :
+				    AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3,
+			      AWIN_LCDx_CHx_CLK_SRC_SEL) |
+		    __SHIFTIN(m - 1, AWIN_LCDx_CH1_CLK_DIV_RATIO_M),
+		    AWIN_LCDx_CH1_CLK_DIV_RATIO_M |
+		    AWIN_LCDx_CHx_CLK_SRC_SEL |
+		    AWIN_LCDx_CH1_SCLK1_SRC_SEL);
+		break;
+	case 7:
+		awin_pll7_set_rate(n * 3000000);
+		awin_reg_set_clear(sc->sc_bst, sc->sc_ch1clk_bsh, 0,
+		    AWIN_CLK_OUT_ENABLE |
+		    AWIN_LCDx_CH1_SCLK1_GATING |
+		    __SHIFTIN(dbl ? AWIN_LCDx_CHx_CLK_SRC_SEL_PLL7_2X :
+				    AWIN_LCDx_CHx_CLK_SRC_SEL_PLL7,
+			      AWIN_LCDx_CHx_CLK_SRC_SEL) |
+		    __SHIFTIN(m - 1, AWIN_LCDx_CH1_CLK_DIV_RATIO_M),
+		    AWIN_LCDx_CH1_CLK_DIV_RATIO_M |
+		    AWIN_LCDx_CHx_CLK_SRC_SEL |
+		    AWIN_LCDx_CH1_SCLK1_SRC_SEL);
+		break;
+	default:
+		panic("awin_tcon pll");
+	}
 
 	sc->sc_clk_div = m;
 	sc->sc_clk_dbl = dbl;
 }
 
 void
-awin_tcon_enable(bool enable)
+awin_tcon_enable(int unit, bool enable)
 {
 	struct awin_tcon_softc *sc;
 	device_t dev;
 	uint32_t val;
 
-	dev = device_find_by_driver_unit("awintcon", 0);
+	dev = device_find_by_driver_unit("awintcon", unit);
 	if (dev == NULL) {
-		printf("TCON: no driver found\n");
+		printf("TCON%d: no driver found\n", unit);
 		return;
 	}
 	sc = device_private(dev);
 
+	awin_debe_enable(sc->sc_debe_unit, enable);
+	delay(20000);
 	val = TCON_READ(sc, AWIN_TCON_GCTL_REG);
 	if (enable) {
 		val |= AWIN_TCON_GCTL_EN;
@@ -219,22 +283,46 @@ awin_tcon_enable(bool enable)
 		val |= 0x03000000;
 	}
 	TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, val);
+
+	KASSERT(tcon_mux_inited);
+	val = bus_space_read_4(sc->sc_bst, tcon_mux_bsh, 0);
+#ifdef AWIN_TCON_DEBUG
+	printf("awin_tcon_enable(%d) val 0x%x", unit, val);
+#endif
+	val &= ~ AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC;
+	if (unit == 0) {
+		val |= __SHIFTIN(AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_LCDC0_TCON1,
+		    AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC);
+	} else if (unit == 1) {
+		val |= __SHIFTIN(AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_LCDC1_TCON1,
+		    AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC);
+	} 
+#ifdef AWIN_TCON_DEBUG
+	printf(" -> 0x%x", val);
+#endif
+	bus_space_write_4(sc->sc_bst, tcon_mux_bsh, 0, val);
+#ifdef AWIN_TCON_DEBUG
+	printf(": 0x%" PRIxBSH " 0x%" PRIxBSH " 0x%x 0x%x\n", sc->sc_bsh,
+	    tcon_mux_bsh, bus_space_read_4(sc->sc_bst, tcon_mux_bsh, 0),
+	    TCON_READ(sc, AWIN_TCON_MUX_CTL_REG));
+#endif
 }
 
 void
-awin_tcon_set_videomode(const struct videomode *mode)
+awin_tcon_set_videomode(int unit, const struct videomode *mode)
 {
 	struct awin_tcon_softc *sc;
 	device_t dev;
 	uint32_t val;
 
-	dev = device_find_by_driver_unit("awintcon", 0);
+	dev = device_find_by_driver_unit("awintcon", unit);
 	if (dev == NULL) {
-		printf("TCON: no driver found\n");
+		printf("TCON%d: no driver found\n", unit);
 		return;
 	}
 	sc = device_private(dev);
 
+	awin_debe_set_videomode(sc->sc_debe_unit, mode);
 	if (mode) {
 		const u_int interlace_p = !!(mode->flags & VID_INTERLACE);
 		const u_int phsync_p = !!(mode->flags & VID_PHSYNC);
@@ -252,17 +340,33 @@ awin_tcon_set_videomode(const struct vid
 		val |= AWIN_TCON_GCTL_IO_MAP_SEL;
 		TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val);
 
+		val = TCON_READ(sc, AWIN_TCON0_CTL_REG);
+		val &= ~0x00400003;
+		if (sc->sc_debe_unit == 0) {
+			val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE0,
+					 AWIN_TCON_CTL_SRC_SEL);
+		} else {
+			val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE1,
+					 AWIN_TCON_CTL_SRC_SEL);
+		}
+		TCON_WRITE(sc, AWIN_TCON0_CTL_REG, val);
+
 		/* enable */
 		val = AWIN_TCON_CTL_EN;
 		if (interlace_p)
 			val |= AWIN_TCON_CTL_INTERLACE_EN;
 		val |= __SHIFTIN(start_delay, AWIN_TCON_CTL_START_DELAY);
-#ifdef AWIN_TCON_BLUEDATA
+#ifdef AWIN_TCON1_BLUEDATA
 		val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_BLUEDATA,
 				 AWIN_TCON_CTL_SRC_SEL);
 #else
-		val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE0,
-				 AWIN_TCON_CTL_SRC_SEL);
+		if (sc->sc_debe_unit == 0) {
+			val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE0,
+					 AWIN_TCON_CTL_SRC_SEL);
+		} else {
+			val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE1,
+					 AWIN_TCON_CTL_SRC_SEL);
+		}
 #endif
 		TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val);
 
@@ -308,6 +412,14 @@ awin_tcon_set_videomode(const struct vid
 
 		/* Setup LCDx CH1 PLL */
 		awin_tcon_set_pll(sc, mode);
+#if 0
+{
+	int i;
+	for (i = 0; i < 0x800; i += 4) {
+		printf("TCON 0x%04x: 0x%08x\n", i, TCON_READ(sc, i));
+	}
+}
+#endif
 	} else {
 		/* disable */
 		val = TCON_READ(sc, AWIN_TCON1_CTL_REG);
@@ -317,14 +429,30 @@ awin_tcon_set_videomode(const struct vid
 }
 
 unsigned int
-awin_tcon_get_clk_div(void)
+awin_tcon_get_clk_pll(int unit)
+{
+	struct awin_tcon_softc *sc;
+	device_t dev;
+
+	dev = device_find_by_driver_unit("awintcon", unit);
+	if (dev == NULL) {
+		printf("TCON%d: no driver found\n", unit);
+		return 0;
+	}
+	sc = device_private(dev);
+
+	return sc->sc_clk_pll;
+}
+
+unsigned int
+awin_tcon_get_clk_div(int unit)
 {
 	struct awin_tcon_softc *sc;
 	device_t dev;
 
-	dev = device_find_by_driver_unit("awintcon", 0);
+	dev = device_find_by_driver_unit("awintcon", unit);
 	if (dev == NULL) {
-		printf("TCON: no driver found\n");
+		printf("TCON%d: no driver found\n", unit);
 		return 0;
 	}
 	sc = device_private(dev);
@@ -333,14 +461,14 @@ awin_tcon_get_clk_div(void)
 }
 
 bool
-awin_tcon_get_clk_dbl(void)
+awin_tcon_get_clk_dbl(int unit)
 {
 	struct awin_tcon_softc *sc;
 	device_t dev;
 
-	dev = device_find_by_driver_unit("awintcon", 0);
+	dev = device_find_by_driver_unit("awintcon", unit);
 	if (dev == NULL) {
-		printf("TCON: no driver found\n");
+		printf("TCON%d: no driver found\n", unit);
 		return 0;
 	}
 	sc = device_private(dev);

Index: src/sys/arch/arm/allwinner/awin_var.h
diff -u src/sys/arch/arm/allwinner/awin_var.h:1.36 src/sys/arch/arm/allwinner/awin_var.h:1.37
--- src/sys/arch/arm/allwinner/awin_var.h:1.36	Sat Oct 17 15:30:14 2015
+++ src/sys/arch/arm/allwinner/awin_var.h	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_var.h,v 1.36 2015/10/17 15:30:14 bouyer Exp $ */
+/* $NetBSD: awin_var.h,v 1.37 2015/10/25 20:54:19 bouyer Exp $ */
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -108,6 +108,7 @@ void	awin_pll3_enable(void);
 void	awin_pll6_enable(void);
 void	awin_pll7_enable(void);
 void	awin_pll3_set_rate(uint32_t);
+void	awin_pll7_set_rate(uint32_t);
 uint32_t awin_pll5x_get_rate(void);
 uint32_t awin_pll6_get_rate(void);
 uint32_t awin_periph0_get_rate(void);
@@ -136,12 +137,13 @@ int	awin_dma_transfer(void *, paddr_t, p
 void	awin_dma_halt(void *);
 
 struct videomode;
-unsigned int awin_tcon_get_clk_div(void);
-bool	awin_tcon_get_clk_dbl(void);
-void	awin_tcon_set_videomode(const struct videomode *);
-void	awin_tcon_enable(bool);
-void	awin_debe_set_videomode(const struct videomode *);
-void	awin_debe_enable(bool);
+unsigned int awin_tcon_get_clk_pll(int);
+unsigned int awin_tcon_get_clk_div(int);
+bool	awin_tcon_get_clk_dbl(int);
+void	awin_tcon_set_videomode(int, const struct videomode *);
+void	awin_tcon_enable(int, bool);
+void	awin_debe_set_videomode(int, const struct videomode *);
+void	awin_debe_enable(int, bool);
 int	awin_debe_ioctl(device_t, u_long, void *);
 int	awin_mp_ioctl(device_t, u_long, void *);
 void	awin_mp_setbase(device_t, paddr_t, size_t);

Index: src/sys/arch/evbarm/awin/awin_machdep.c
diff -u src/sys/arch/evbarm/awin/awin_machdep.c:1.43 src/sys/arch/evbarm/awin/awin_machdep.c:1.44
--- src/sys/arch/evbarm/awin/awin_machdep.c:1.43	Wed Oct 21 09:25:16 2015
+++ src/sys/arch/evbarm/awin/awin_machdep.c	Sun Oct 25 20:54:19 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: awin_machdep.c,v 1.43 2015/10/21 09:25:16 jmcneill Exp $ */
+/*	$NetBSD: awin_machdep.c,v 1.44 2015/10/25 20:54:19 bouyer 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.43 2015/10/21 09:25:16 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_machdep.c,v 1.44 2015/10/25 20:54:19 bouyer Exp $");
 
 #include "opt_machdep.h"
 #include "opt_ddb.h"
@@ -260,6 +260,10 @@ static void awin_device_register(device_
 
 #ifdef AWIN_SYSCONFIG
 static void awin_gpio_sysconfig(prop_dictionary_t);
+static void awin_display_sysconfig(prop_dictionary_t);
+static void awin_hdmi_sysconfig(prop_dictionary_t);
+static void awin_tcon_sysconfig(device_t, prop_dictionary_t);
+static void awin_tcon_lcd_sysconfig(const char *, prop_dictionary_t);
 #endif
 
 #if NCOM > 0
@@ -673,6 +677,11 @@ awin_device_register(device_t self, void
 #elif AWIN_board == AWIN_bpi || AWIN_board == AWIN_olimexlime2
 		prop_dictionary_set_bool(dict, "no-awe", true);
 #endif
+#ifdef AWIN_SYSCONFIG
+		if (awin_sysconfig_p) {
+			awin_display_sysconfig(dict);
+		}
+#endif
 		return;
 	}
 
@@ -934,4 +943,217 @@ awin_gpio_sysconfig(prop_dictionary_t di
 		}
 	}
 }
+
+/* see which display devices needs to be disabled */
+
+static void
+awin_display_sysconfig(prop_dictionary_t dict)
+{
+	bool hdmi_used = false;
+	int screen0_type, screen1_type;
+
+	switch(awin_sysconfig_get_int("disp_init", "disp_init_enable")) {
+	case -1:
+		return;
+	case 0:
+		prop_dictionary_set_bool(dict, "no-awindebe-0", true);
+		prop_dictionary_set_bool(dict, "no-awindebe-1", true);
+		prop_dictionary_set_bool(dict, "no-awintcon-0", true);
+		prop_dictionary_set_bool(dict, "no-awintcon-1", true);
+		prop_dictionary_set_bool(dict, "no-awinhdmi", true);
+		prop_dictionary_set_bool(dict, "no-awinhdmiaudio", true);
+		return;
+	default:
+		break;
+	}
+	screen0_type = awin_sysconfig_get_int("disp_init", "screen0_output_type");
+	screen1_type = awin_sysconfig_get_int("disp_init", "screen1_output_type");
+	switch(awin_sysconfig_get_int("disp_init", "disp_mode")) {
+	case 0:
+		/* screen0, fb0 */
+		prop_dictionary_set_bool(dict, "no-awindebe-1", true);
+		prop_dictionary_set_bool(dict, "no-awintcon-1", true);
+		hdmi_used = (screen0_type == 3);
+		break;
+	case 1:
+		/* screen1, fb0 */
+		prop_dictionary_set_bool(dict, "no-awindebe-1", true);
+		prop_dictionary_set_bool(dict, "no-awintcon-0", true);
+		hdmi_used = (screen1_type == 3);
+		break;
+	case 2:
+		/* dual-head; all tcon and debe used */
+		hdmi_used = (screen0_type == 3 || screen1_type == 3);
+		break;
+	case 3:
+		/* xinerama */
+	case 4:
+		/* clone */
+		prop_dictionary_set_bool(dict, "no-awindebe-1", true);
+		hdmi_used = (screen0_type == 3 || screen1_type == 3);
+		break;
+	default:
+		return;
+	}
+	if (!hdmi_used) {
+		prop_dictionary_set_bool(dict, "no-awinhdmi", true);
+		prop_dictionary_set_bool(dict, "no-awinhdmiaudio", true);
+	}
+
+}
+
+static void
+awin_hdmi_sysconfig(prop_dictionary_t dict)
+{
+	int type;
+
+	if (awin_sysconfig_get_int("disp_init", "disp_mode") != 1) {
+		/* tcon0 enabled, try tcon0 first */
+		type =
+		    awin_sysconfig_get_int("disp_init", "screen0_output_type");
+		if (type < 0)
+			return;
+		if (type == 3) {
+			prop_dictionary_set_int8(dict, "tcon_unit", 0);
+			return;
+		}
+	}
+	/* either tcon0 is not enabled, or not in hdmi mode. try tcon1 */
+	type = awin_sysconfig_get_int("disp_init", "screen1_output_type");
+	if (type == 3) {
+		prop_dictionary_set_int8(dict, "tcon_unit", 1);
+		return;
+	}
+	/*
+	 * all other cases, including failure to get screen1_output_type
+	 * Note that this should not happen as HDMI should have been
+	 * disabled in this case.
+	 */
+	prop_dictionary_set_int8(dict, "tcon_unit", -1);
+}
+
+static void
+awin_tcon_sysconfig(device_t self, prop_dictionary_t dict)
+{
+	int mode = awin_sysconfig_get_int("disp_init", "disp_mode");
+	int type;
+
+	if (device_unit(self) == 0) {
+		if (mode < 0)
+			return;
+
+		prop_dictionary_set_int8(dict, "debe_unit", 0);
+		type = awin_sysconfig_get_int("disp_init", "screen0_output_type");
+		if (type == 1) {
+			/* LCD/LVDS output */
+			awin_tcon_lcd_sysconfig("lcd0_para", dict);
+			return;
+		}
+		if (type == 3) {
+			prop_dictionary_set_cstring(dict, "output", "hdmi");
+			return;
+		}
+		/* unsupported mode */
+		return;
+	}
+	if (device_unit(self) == 1) {
+		switch (mode) {
+		case 0:
+			/* only mode where tcon1 is not used */
+			return;
+		case 2:
+			prop_dictionary_set_int8(dict, "debe_unit", 1);
+			break;
+		default:
+			prop_dictionary_set_int8(dict, "debe_unit", 0);
+			break;
+		}
+		type = awin_sysconfig_get_int("disp_init", "screen1_output_type");
+		if (type == 1) {
+			/* LCD/LVDS output */
+			awin_tcon_lcd_sysconfig("lcd1_para", dict);
+			return;
+		}
+		if (type == 3) {
+			prop_dictionary_set_cstring(dict, "output", "hdmi");
+			return;
+		}
+		/* unsupported mode */
+		return;
+	}
+}
+
+static void
+awin_tcon_lcd_sysconfig(const char *key, prop_dictionary_t dict)
+{
+	static const char *lcdtimings[] = {
+		"lcd_x",
+		"lcd_y",
+		"lcd_dclk_freq",
+		"lcd_hbp",
+		"lcd_ht",
+		"lcd_vbp",
+		"lcd_vt",
+		"lcd_hv_hspw",
+		"lcd_io_cfg0",
+	};
+	static const char *lcdgpio[] = {
+		"lcdd0",
+		"lcdd1",
+		"lcdd2",
+		"lcdd3",
+		"lcdd4",
+		"lcdd5",
+		"lcdd6",
+		"lcdd7",
+		"lcdd8",
+		"lcdd9",
+		"lcdd10",
+		"lcdd11",
+		"lcdd12",
+		"lcdd13",
+		"lcdd14",
+		"lcdd15",
+		"lcdd16",
+		"lcdd17",
+		"lcdd18",
+		"lcdd19",
+		"lcdd20",
+		"lcdd21",
+		"lcdd22",
+		"lcdd23",
+		"lcdclk",
+		"lcdde",
+		"lcdhsync",
+		"lcdvsync"
+	};
+	unsigned int n;
+	const char *cfg;
+
+	for (n = 0; n < __arraycount(lcdtimings); n++) {
+		int value = awin_sysconfig_get_int( key, lcdtimings[n]);
+		if (value >= 0) {
+			prop_dictionary_set_int32(dict, lcdtimings[n], value);
+		}
+	}
+	if (awin_sysconfig_get_int(key, "lcd_bl_en_used") == 1) {
+		cfg = awin_sysconfig_get_gpio(key, "lcd_bl_en");
+		if (cfg != NULL) {
+			prop_dictionary_set_cstring(dict, "lcd_bl_en", cfg);
+		}
+	}
+	if (awin_sysconfig_get_int(key, "lcd_power_used") == 1) {
+		cfg = awin_sysconfig_get_gpio(key, "lcd_power");
+		if (cfg != NULL) {
+			prop_dictionary_set_cstring(dict, "lcd_bl_en", cfg);
+		}
+	}
+	for (n = 0; n < __arraycount(lcdgpio); n++) {
+		cfg = awin_sysconfig_get_string(key, lcdgpio[n]);
+		if (cfg != NULL) {
+			prop_dictionary_set_cstring(dict, lcdgpio[n], cfg);
+		}
+	}
+
+}
 #endif

Reply via email to