Module Name:    src
Committed By:   bouyer
Date:           Sun Nov 15 21:28:54 UTC 2015

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

Log Message:
Add support for LVDS output on tcon0. Tested with a full HD LVDS display.
Other LCD interfaces not supported yet, mostly by lack of hardware.
The LVDS output and timings are set from the FEX script.


To generate a diff of this commit:
cvs rdiff -u -r1.17 -r1.18 src/sys/arch/arm/allwinner/awin_hdmi.c
cvs rdiff -u -r1.83 -r1.84 src/sys/arch/arm/allwinner/awin_reg.h
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/arm/allwinner/awin_tcon.c
cvs rdiff -u -r1.37 -r1.38 src/sys/arch/arm/allwinner/awin_var.h
cvs rdiff -u -r1.46 -r1.47 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_hdmi.c
diff -u src/sys/arch/arm/allwinner/awin_hdmi.c:1.17 src/sys/arch/arm/allwinner/awin_hdmi.c:1.18
--- src/sys/arch/arm/allwinner/awin_hdmi.c:1.17	Sun Oct 25 20:54:19 2015
+++ src/sys/arch/arm/allwinner/awin_hdmi.c	Sun Nov 15 21:28:54 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_hdmi.c,v 1.17 2015/10/25 20:54:19 bouyer Exp $ */
+/* $NetBSD: awin_hdmi.c,v 1.18 2015/11/15 21:28:54 bouyer Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -30,7 +30,7 @@
 #include "opt_ddb.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.17 2015/10/25 20:54:19 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_hdmi.c,v 1.18 2015/11/15 21:28:54 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -580,13 +580,13 @@ awin_hdmi_read_edid(struct awin_hdmi_sof
 
 	if (mode != NULL) {
 		awin_hdmi_video_enable(sc, false);
-		awin_tcon_enable(sc->sc_tcon_unit, false);
+		awin_tcon1_enable(sc->sc_tcon_unit, false);
 		delay(20000);
 
-		awin_tcon_set_videomode(sc->sc_tcon_unit, mode);
+		awin_tcon1_set_videomode(sc->sc_tcon_unit, mode);
 		awin_hdmi_set_videomode(sc, mode, display_mode);
 		awin_hdmi_set_audiomode(sc, mode, display_mode);
-		awin_tcon_enable(sc->sc_tcon_unit, true);
+		awin_tcon1_enable(sc->sc_tcon_unit, true);
 		delay(20000);
 		awin_hdmi_video_enable(sc, true);
 	}
@@ -928,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(sc->sc_tcon_unit, NULL);
+		awin_tcon1_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.83 src/sys/arch/arm/allwinner/awin_reg.h:1.84
--- src/sys/arch/arm/allwinner/awin_reg.h:1.83	Mon Nov  9 10:10:39 2015
+++ src/sys/arch/arm/allwinner/awin_reg.h	Sun Nov 15 21:28:54 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_reg.h,v 1.83 2015/11/09 10:10:39 bouyer Exp $ */
+/* $NetBSD: awin_reg.h,v 1.84 2015/11/15 21:28:54 bouyer Exp $ */
 
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -1111,6 +1111,8 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_CLK_DIV_RATIO_N		__BITS(17,16)
 #define AWIN_CLK_DIV_RATIO_M		__BITS(3,0)
 
+#define AWIN_LVDS_CLK_ENABLE		__BIT(0)
+
 #define AWIN_ISS_CLK_SRC_SEL		__BITS(17,16)
 
 #define AWIN_USB_CLK_USBPHY_ENABLE	__BIT(8)
@@ -1161,7 +1163,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_PLL7_2X 3
+#define AWIN_LCDx_CHx_CLK_SRC_SEL_PLL7_2X 3 /* for lcd0ch0 this is pll6x2 */
 #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)
@@ -1772,6 +1774,8 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_TCON_GINT0_REG		0x0004
 #define AWIN_TCON_GINT1_REG		0x0008
 #define AWIN_TCON0_FRM_CTL_REG		0x0010
+#define AWIN_TCON0_FRM1_CTL_REG		0x0014
+#define AWIN_TCON0_FRM2_CTL_REG		0x002c
 #define AWIN_TCON0_CTL_REG		0x0040
 #define AWIN_TCON0_DCLK_REG		0x0044
 #define AWIN_TCON0_BASIC0_REG		0x0048
@@ -1806,6 +1810,8 @@ struct awin_mmc_idma_descriptor {
 #define AWIN_TCON_CMAP_EVEN0_REG	0x0198
 #define AWIN_TCON_CMAP_EVEN1_REG	0x019C
 #define AWIN_TCON_MUX_CTL_REG		0x0200 /* only in TCON0 */
+#define AWIN_TCON_LVDS_ANA0		0x220
+#define AWIN_TCON_LVDS_ANA1		0x224
 
 #define AWIN_TCON_GCTL_EN		__BIT(31)
 #define AWIN_TCON_GCTL_GAMMA_EN		__BIT(30)
@@ -1816,13 +1822,33 @@ struct awin_mmc_idma_descriptor {
 #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_INTERLACE_EN	__BIT(20)
-#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_TCON0_FRM_ENABLE		__BIT(31)
+#define AWIN_TCON0_FRM_R5BITS		__BIT(6)
+#define AWIN_TCON0_FRM_G5BITS		__BIT(5)
+#define AWIN_TCON0_FRM_B5BITS		__BIT(4)
+
+#define AWIN_TCONx_CTL_EN		__BIT(31)
+#define AWIN_TCON0_CTL0_IF		__BITS(25,24)
+#define AWIN_TCON0_CTL0_IF_HV		0
+#define AWIN_TCON0_CTL0_IF_8080		1
+#define AWIN_TCON0_CTL0_IF_TTL		2
+#define AWIN_TCON0_CTL_RG_SWAP		__BIT(23)
+#define AWIN_TCON0_CTL_TSTV		__BIT(22)
+#define AWIN_TCONx_CTL_INTERLACE_EN	__BIT(20)
+#define AWIN_TCONx_CTL_START_DELAY	__BITS(8,4)
+#define AWIN_TCONx_CTL_SRC_SEL		__BITS(1,0)
+#define AWIN_TCONx_CTL_SRC_SEL_DE0	0
+#define AWIN_TCONx_CTL_SRC_SEL_DE1	1
+#define AWIN_TCONx_CTL_SRC_SEL_BLUEDATA	2
+
+#define AWIN_TCON0_DCLK_DIV		__BITS(6,0)
+
+#define AWIN_TCON0_LVDS_IF_EN		__BIT(31)
+#define AWIN_TCON0_LVDS_IF_DUALCHAN	__BIT(30)
+#define AWIN_TCON0_LVDS_IF_DIR_REV	__BIT(28)
+#define AWIN_TCON0_LVDS_IF_MODE_JEIDA	__BIT(27)
+#define AWIN_TCON0_LVDS_IF_18BITS	__BIT(26)
+#define AWIN_TCON0_LVDS_IF_CORR_MODE1	__BIT(23)
 
 #define AWIN_TCON_IO_POL_IO2_INV	__BIT(26)
 #define AWIN_TCON_IO_POL_PVSYNC		__BIT(25)

Index: src/sys/arch/arm/allwinner/awin_tcon.c
diff -u src/sys/arch/arm/allwinner/awin_tcon.c:1.8 src/sys/arch/arm/allwinner/awin_tcon.c:1.9
--- src/sys/arch/arm/allwinner/awin_tcon.c:1.8	Tue Nov  3 19:28:28 2015
+++ src/sys/arch/arm/allwinner/awin_tcon.c	Sun Nov 15 21:28:54 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_tcon.c,v 1.8 2015/11/03 19:28:28 bouyer Exp $ */
+/* $NetBSD: awin_tcon.c,v 1.9 2015/11/15 21:28:54 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.8 2015/11/03 19:28:28 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_tcon.c,v 1.9 2015/11/15 21:28:54 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -47,17 +47,37 @@ __KERNEL_RCSID(0, "$NetBSD: awin_tcon.c,
 
 #define DIVIDE(x,y)     (((x) + ((y) / 2)) / (y))
 
+struct awin_tcon_gpio {
+	const char *value;
+	const char *name;
+};
+
+
 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_ch0clk_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;
+	unsigned int sc_output_type;
+#define OUTPUT_HDMI 0
+#define OUTPUT_LVDS 1
+	const char *sc_lcdpwr_pin_name;
+	struct awin_gpio_pindata sc_lcdpwr_pin;
+	const char *sc_lcdblk_pin_name;
+	struct awin_gpio_pindata sc_lcdblk_pin;
 };
 
+static const struct awin_gpio_pinset awin_lvds0_pinset = 
+	{ 'D', AWIN_PIO_PD_LVDS0_FUNC, AWIN_PIO_PD_LVDS0_PINS};
+static const struct awin_gpio_pinset awin_lvds1_pinset = 
+	{ 'D', AWIN_PIO_PD_LVDS1_FUNC, AWIN_PIO_PD_LVDS1_PINS};
+
 static bus_space_handle_t tcon_mux_bsh;
 static bool tcon_mux_inited = false;
 
@@ -69,8 +89,8 @@ static bool tcon_mux_inited = false;
 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 *,
-				  const struct videomode *);
+static void	awin_tcon_set_pll(struct awin_tcon_softc *, int, int);
+static void	awin_tcon0_set_video(struct awin_tcon_softc *);
 
 static void
 awin_tcon_clear_reset(struct awinio_attach_args * const aio, int unit)
@@ -112,6 +132,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);
+	const char *output;
 
 	sc->sc_dev = self;
 	sc->sc_bst = aio->aio_core_bst;
@@ -119,6 +141,8 @@ awin_tcon_attach(device_t parent, device
 	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,
+	    AWIN_LCD0_CH0_CLK_REG + (loc->loc_port * 4), 4, &sc->sc_ch0clk_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 */
@@ -129,6 +153,18 @@ awin_tcon_attach(device_t parent, device
 		awin_tcon_clear_reset(aio, 0);
 	}
 
+	if (prop_dictionary_get_cstring_nocopy(cfg, "output", &output)) {
+		if (strcmp(output, "hdmi") == 0) {
+			sc->sc_output_type = OUTPUT_HDMI;
+		} else if (strcmp(output, "lvds") == 0) {
+			sc->sc_output_type = OUTPUT_LVDS;
+		} else {
+			panic("tcon: wrong mode %s", output);
+		}
+	} else {
+		sc->sc_output_type = OUTPUT_HDMI; /* default */
+	}
+
 	aprint_naive("\n");
 	aprint_normal(": LCD/TV timing controller (TCON%d)\n", loc->loc_port);
 	switch (sc->sc_port) {
@@ -152,17 +188,25 @@ awin_tcon_attach(device_t parent, device
 	TCON_WRITE(sc, AWIN_TCON_GCTL_REG, 0);
 	TCON_WRITE(sc, AWIN_TCON_GINT0_REG, 0);
 	TCON_WRITE(sc, AWIN_TCON_GINT1_REG,
-	    __SHIFTIN(0x20, AWIN_TCON_GINT1_TCON1_LINENO));
+	    __SHIFTIN(0x20, AWIN_TCON_GINT1_TCON0_LINENO));
 	TCON_WRITE(sc, AWIN_TCON0_DCLK_REG, 0xf0000000);
+	TCON_WRITE(sc, AWIN_TCON0_CTL_REG, 0);
 	TCON_WRITE(sc, AWIN_TCON0_IO_TRI_REG, 0xffffffff);
+	TCON_WRITE(sc, AWIN_TCON1_CTL_REG, 0);
 	TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, 0xffffffff);
+	if (sc->sc_output_type == OUTPUT_LVDS) {
+		awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
+		    AWIN_LVDS_CLK_REG, AWIN_LVDS_CLK_ENABLE, 0);
+		awin_tcon0_set_video(sc);
+	}
 }
 
 static void
-awin_tcon_calc_pll(int f_ref, int f_out, int *pm, int *pn)
+awin_tcon_calc_pll(int f_ref, int f_out, int min_m, int *pm, int *pn)
 {
 	int best = 1000000;
-	for (int m = 1; m <= 15; m++) {
+	KASSERT(min_m > 0);
+	for (int m = min_m; m <= 15; m++) {
 		for (int n = 9; n <= 127; n++) {
 			int f_cur = (n * f_ref) / m;
 			int diff = f_out - f_cur;
@@ -176,13 +220,13 @@ awin_tcon_calc_pll(int f_ref, int f_out,
 }
 
 static void
-awin_tcon_set_pll(struct awin_tcon_softc *sc, const struct videomode *mode)
+awin_tcon_set_pll(struct awin_tcon_softc *sc, int dclk, int min_div)
 {
 	int n = 0, m = 0, n2 = 0, m2 = 0;
 	bool dbl = false;
 
-	awin_tcon_calc_pll(3000, mode->dot_clock, &m, &n);
-	awin_tcon_calc_pll(6000, mode->dot_clock, &m2, &n2);
+	awin_tcon_calc_pll(3000, dclk, min_div, &m, &n);
+	awin_tcon_calc_pll(6000, dclk, min_div, &m2, &n2);
 
 	int f_single = m ? (n * 3000) / m : 0;
 	int f_double = m2 ? (n2 * 6000) / m2 : 0;
@@ -195,42 +239,83 @@ awin_tcon_set_pll(struct awin_tcon_softc
 
 	if (n == 0 || m == 0) {
 		device_printf(sc->sc_dev, "couldn't set pll to %d Hz\n",
-		    mode->dot_clock * 1000);
+		    dclk * 1000);
 		sc->sc_clk_div = 0;
 		return;
 	}
 
 #ifdef AWIN_TCON_DEBUG
-	device_printf(sc->sc_dev, "pll n=%d m=%d dbl=%c freq=%d\n", n, m,
-	    dbl ? 'Y' : 'N', n * 3000000);
+	device_printf(sc->sc_dev, "ch%d pll%d n=%d m=%d dbl=%c freq=%d\n",
+	    (sc->sc_output_type == OUTPUT_HDMI) ? 1 : 0,
+	    sc->sc_clk_pll, n, m, dbl ? 'Y' : 'N', n * 3000000);
 #endif
 
 	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);
+		if (sc->sc_output_type == OUTPUT_HDMI) {
+			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);
+		} else {
+			awin_reg_set_clear(sc->sc_bst, sc->sc_ch0clk_bsh, 0,
+			    AWIN_CLK_OUT_ENABLE | AWIN_LCDx_CH0_CLK_LCDx_RST |
+			    __SHIFTIN(dbl ? AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3_2X :
+					    AWIN_LCDx_CHx_CLK_SRC_SEL_PLL3,
+				      AWIN_LCDx_CHx_CLK_SRC_SEL),
+			    AWIN_LCDx_CHx_CLK_SRC_SEL);
+			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(10, 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);
+		if (sc->sc_output_type == OUTPUT_HDMI) {
+			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);
+		} else {
+			/* pll7x2 not available for lcd0ch0 */
+			KASSERT(dbl == false || sc->sc_port != 0);
+			awin_reg_set_clear(sc->sc_bst, sc->sc_ch0clk_bsh, 0,
+			    AWIN_CLK_OUT_ENABLE | AWIN_LCDx_CH0_CLK_LCDx_RST |
+			    __SHIFTIN(dbl ? AWIN_LCDx_CHx_CLK_SRC_SEL_PLL7_2X :
+					    AWIN_LCDx_CHx_CLK_SRC_SEL_PLL7,
+				      AWIN_LCDx_CHx_CLK_SRC_SEL),
+			    AWIN_LCDx_CHx_CLK_SRC_SEL);
+			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(10, 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");
@@ -240,8 +325,220 @@ awin_tcon_set_pll(struct awin_tcon_softc
 	sc->sc_clk_dbl = dbl;
 }
 
+static void
+awin_tcon0_set_video(struct awin_tcon_softc *sc)
+{
+	int32_t lcd_x, lcd_y, lcd_dclk_freq;
+	int32_t lcd_hbp, lcd_ht, lcd_vbp, lcd_vt;
+	int32_t lcd_hspw, lcd_vspw, lcd_io_cfg0;
+	uint32_t vblk, start_delay;
+	prop_dictionary_t cfg = device_properties(sc->sc_dev);
+	uint32_t val;
+	bool propb;
+	bool dualchan = false;
+	static struct videomode mode;
+
+	if (!prop_dictionary_get_int32(cfg, "lcd_x", &lcd_x)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_x\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_y", &lcd_y)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_y\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_dclk_freq", &lcd_dclk_freq)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_dclk_freq\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_hbp", &lcd_hbp)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_hbp\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_ht", &lcd_ht)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_ht\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_vbp", &lcd_vbp)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_vbp\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_vt", &lcd_vt)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_vt\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_hspw", &lcd_hspw)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_hspw\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_vspw", &lcd_vspw)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_vspw\n");
+		return;
+	}
+	if (!prop_dictionary_get_int32(cfg, "lcd_io_cfg0", &lcd_io_cfg0)) {
+		aprint_error_dev(sc->sc_dev, ": can't read lcd_io_cfg0\n");
+		return;
+	}
+
+	if (prop_dictionary_get_bool(cfg, "lvds_dual", &propb) && propb)
+		dualchan = true;
+	if (!awin_gpio_pinset_available(&awin_lvds0_pinset)) {
+		aprint_error_dev(sc->sc_dev, "lvds0 pins not available\n");
+		return;
+	}
+	if (dualchan && !awin_gpio_pinset_available(&awin_lvds1_pinset)) {
+		aprint_error_dev(sc->sc_dev, "lvds1 pins not available\n");
+		return;
+	}
+	awin_gpio_pinset_acquire(&awin_lvds0_pinset);
+	if (dualchan) {
+		awin_gpio_pinset_acquire(&awin_lvds1_pinset);
+	}
+	prop_dictionary_get_cstring_nocopy(cfg, "lcd_power_en",
+	    &sc->sc_lcdpwr_pin_name);
+	if (sc->sc_lcdpwr_pin_name != NULL) {
+		if (!awin_gpio_pin_reserve(
+		    sc->sc_lcdpwr_pin_name, &sc->sc_lcdpwr_pin)) {
+			aprint_error_dev(sc->sc_dev,
+			    "failed to reserve GPIO \"%s\" for LCD power\n",
+			    sc->sc_lcdpwr_pin_name);
+			sc->sc_lcdpwr_pin_name = NULL;
+		} else {
+			aprint_verbose_dev(sc->sc_dev,
+			    ": using GPIO \"%s\" for LCD power\n", 
+			    sc->sc_lcdpwr_pin_name);
+		}
+	}
+	prop_dictionary_get_cstring_nocopy(cfg, "lcd_bl_en",
+	    &sc->sc_lcdblk_pin_name);
+	if (sc->sc_lcdblk_pin_name != NULL) {
+		if (!awin_gpio_pin_reserve(
+		    sc->sc_lcdblk_pin_name, &sc->sc_lcdblk_pin)) {
+			aprint_error_dev(sc->sc_dev,
+			    "failed to reserve GPIO \"%s\" for backlight\n",
+			    sc->sc_lcdblk_pin_name);
+			sc->sc_lcdblk_pin_name = NULL;
+		} else {
+			if (sc->sc_lcdpwr_pin_name == NULL) {
+				aprint_verbose_dev(sc->sc_dev,
+				    ": using GPIO \"%s\" for backlight\n", 
+				    sc->sc_lcdblk_pin_name);
+			} else {
+				aprint_verbose(
+				    ", GPIO \"%s\" for backlight\n", 
+				    sc->sc_lcdblk_pin_name);
+			}
+		}
+	}
+
+	if (sc->sc_lcdpwr_pin_name != NULL) {
+		awin_gpio_pindata_write(&sc->sc_lcdpwr_pin, 1);
+	}
+
+	vblk = (lcd_vt / 2) - lcd_y;
+	start_delay = (vblk >= 32) ? 30 : (vblk - 2);
+
+	if (lcd_dclk_freq > 150) /* hardware limit ? */
+		lcd_dclk_freq = 150;
+	awin_tcon_set_pll(sc, lcd_dclk_freq * 1000, 7);
+
+	val = AWIN_TCONx_CTL_EN;
+	val |= __SHIFTIN(start_delay, AWIN_TCONx_CTL_START_DELAY);
+	/*
+	 * the DE selector selects the primary DEBE for this tcon:
+	 * 0 selects debe0 for tcon0 and debe1 for tcon1
+	 */
+	val |= __SHIFTIN(AWIN_TCONx_CTL_SRC_SEL_DE0,
+			 AWIN_TCONx_CTL_SRC_SEL);
+	TCON_WRITE(sc, AWIN_TCON0_CTL_REG, val);
+
+	val =  (lcd_x - 1) << 16 |  (lcd_y - 1);
+	TCON_WRITE(sc, AWIN_TCON0_BASIC0_REG, val);
+	val = (lcd_ht - 1) << 16 | (lcd_hbp - 1);
+	TCON_WRITE(sc, AWIN_TCON0_BASIC1_REG, val);
+	val = (lcd_vt) << 16 | (lcd_vbp - 1);
+	TCON_WRITE(sc, AWIN_TCON0_BASIC2_REG, val);
+	val =  ((lcd_hspw > 0) ? (lcd_hspw - 1) : 0) << 16;
+	val |= ((lcd_vspw > 0) ? (lcd_vspw - 1) : 0);
+	TCON_WRITE(sc, AWIN_TCON0_BASIC3_REG, val);
+
+	val = 0;
+	if (dualchan)
+		val |= AWIN_TCON0_LVDS_IF_DUALCHAN;
+	if (prop_dictionary_get_bool(cfg, "lvds_mode_jeida", &propb) && propb)
+		val |= AWIN_TCON0_LVDS_IF_MODE_JEIDA;
+	if (prop_dictionary_get_bool(cfg, "lvds_18bits", &propb) && propb)
+		val |= AWIN_TCON0_LVDS_IF_18BITS;
+	TCON_WRITE(sc, AWIN_TCON0_LVDS_IF_REG, val);
+
+
+	TCON_WRITE(sc, AWIN_TCON0_IO_POL_REG, lcd_io_cfg0);
+	TCON_WRITE(sc, AWIN_TCON0_IO_TRI_REG, 0);
+	TCON_WRITE(sc, AWIN_TCON_GINT1_REG,
+	    __SHIFTIN(start_delay + 2, AWIN_TCON_GINT1_TCON0_LINENO));
+
+	val = 0xf0000000;
+	val &= ~AWIN_TCON0_DCLK_DIV;
+	val |= __SHIFTIN(sc->sc_clk_div, AWIN_TCON0_DCLK_DIV);
+	TCON_WRITE(sc, AWIN_TCON0_DCLK_REG, val);
+
+	mode.dot_clock = lcd_dclk_freq;
+	mode.hdisplay = lcd_x;
+	mode.hsync_start = lcd_ht - lcd_hbp;
+	mode.hsync_end = lcd_hspw + mode.hsync_start;
+	mode.htotal = lcd_ht;
+	mode.vdisplay = lcd_y;
+	mode.vsync_start = lcd_vt - lcd_vbp;
+	mode.vsync_end = lcd_vspw + mode.vsync_start;
+	mode.vtotal = lcd_vt;
+	mode.flags = 0;
+	mode.name = NULL;
+
+	awin_debe_set_videomode(sc->sc_debe_unit, &mode);
+
+	/* and finally, enable it */
+	awin_debe_enable(sc->sc_debe_unit, true);
+	delay(20000);
+
+	val = TCON_READ(sc, AWIN_TCON_GCTL_REG);
+	val |= AWIN_TCON_GCTL_EN;
+	TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val);
+	delay(20000);
+
+
+	val = TCON_READ(sc, AWIN_TCON0_LVDS_IF_REG);
+	val |= AWIN_TCON0_LVDS_IF_EN;
+	TCON_WRITE(sc, AWIN_TCON0_LVDS_IF_REG, val);
+
+	/* XXX
+	 * magic values here from linux. these are not documented
+	 * in the A20 user manual, and other Allwiner LVDS-capable SoC
+	 * documentation don't make sense with these values
+	 */
+	val = TCON_READ(sc, AWIN_TCON_LVDS_ANA0);
+	val |= 0x3F310000;
+	TCON_WRITE(sc, AWIN_TCON_LVDS_ANA0, val);
+	val = TCON_READ(sc, AWIN_TCON_LVDS_ANA0);
+	val |= 1 << 22;
+	TCON_WRITE(sc, AWIN_TCON_LVDS_ANA0, val);
+	delay(2);
+	val = TCON_READ(sc, AWIN_TCON_LVDS_ANA1);
+	val |= (0x1f << 26 | 0x1f << 10);
+	TCON_WRITE(sc, AWIN_TCON_LVDS_ANA1, val);
+	delay(2);
+	val = TCON_READ(sc, AWIN_TCON_LVDS_ANA1);
+	val |= (0x1f << 16 | 0x1f << 0);
+	TCON_WRITE(sc, AWIN_TCON_LVDS_ANA1, val);
+	val = TCON_READ(sc, AWIN_TCON_LVDS_ANA0);
+	val |= 1 << 22;
+	TCON_WRITE(sc, AWIN_TCON_LVDS_ANA0, val);
+
+	if (sc->sc_lcdblk_pin_name != NULL) {
+		awin_gpio_pindata_write(&sc->sc_lcdblk_pin, 1);
+	}
+}
+
 void
-awin_tcon_enable(int unit, bool enable)
+awin_tcon1_enable(int unit, bool enable)
 {
 	struct awin_tcon_softc *sc;
 	device_t dev;
@@ -253,6 +550,7 @@ awin_tcon_enable(int unit, bool enable)
 		return;
 	}
 	sc = device_private(dev);
+	KASSERT(sc->sc_output_type == OUTPUT_HDMI);
 
 	awin_debe_enable(device_unit(sc->sc_dev), enable);
 	delay(20000);
@@ -264,18 +562,12 @@ awin_tcon_enable(int unit, bool enable)
 	}
 	TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val);
 
-	val = TCON_READ(sc, AWIN_TCON1_IO_TRI_REG);
-	if (enable) {
-		val &= ~0x03000000;
-	} else {
-		val |= 0x03000000;
-	}
-	TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, val);
+	TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, 0);
 
 	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);
+	printf("awin_tcon1_enable(%d) val 0x%x", unit, val);
 #endif
 	val &= ~ AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC;
 	if (unit == 0) {
@@ -297,7 +589,7 @@ awin_tcon_enable(int unit, bool enable)
 }
 
 void
-awin_tcon_set_videomode(int unit, const struct videomode *mode)
+awin_tcon1_set_videomode(int unit, const struct videomode *mode)
 {
 	struct awin_tcon_softc *sc;
 	device_t dev;
@@ -309,6 +601,7 @@ awin_tcon_set_videomode(int unit, const 
 		return;
 	}
 	sc = device_private(dev);
+	KASSERT(sc->sc_output_type == OUTPUT_HDMI);
 
 	awin_debe_set_videomode(device_unit(sc->sc_dev), mode);
 	if (mode) {
@@ -329,20 +622,20 @@ awin_tcon_set_videomode(int unit, const 
 		TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val);
 
 		/* enable */
-		val = AWIN_TCON_CTL_EN;
+		val = AWIN_TCONx_CTL_EN;
 		if (interlace_p)
-			val |= AWIN_TCON_CTL_INTERLACE_EN;
-		val |= __SHIFTIN(start_delay, AWIN_TCON_CTL_START_DELAY);
+			val |= AWIN_TCONx_CTL_INTERLACE_EN;
+		val |= __SHIFTIN(start_delay, AWIN_TCONx_CTL_START_DELAY);
 #ifdef AWIN_TCON1_BLUEDATA
-		val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_BLUEDATA,
-				 AWIN_TCON_CTL_SRC_SEL);
+		val |= __SHIFTIN(AWIN_TCONx_CTL_SRC_SEL_BLUEDATA,
+				 AWIN_TCONx_CTL_SRC_SEL);
 #else
 		/*
 		 * the DE selector selects the primary DEBE for this tcon:
 		 * 0 selects debe0 for tcon0 and debe1 for tcon1
 		 */
-		val |= __SHIFTIN(AWIN_TCON_CTL_SRC_SEL_DE0,
-				 AWIN_TCON_CTL_SRC_SEL);
+		val |= __SHIFTIN(AWIN_TCONx_CTL_SRC_SEL_DE0,
+				 AWIN_TCONx_CTL_SRC_SEL);
 #endif
 		TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val);
 
@@ -387,19 +680,11 @@ awin_tcon_set_videomode(int unit, const 
 		    __SHIFTIN(start_delay + 2, AWIN_TCON_GINT1_TCON1_LINENO));
 
 		/* 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
+		awin_tcon_set_pll(sc, mode->dot_clock, 1);
 	} else {
 		/* disable */
 		val = TCON_READ(sc, AWIN_TCON1_CTL_REG);
-		val &= ~AWIN_TCON_CTL_EN;
+		val &= ~AWIN_TCONx_CTL_EN;
 		TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val);
 	}
 }

Index: src/sys/arch/arm/allwinner/awin_var.h
diff -u src/sys/arch/arm/allwinner/awin_var.h:1.37 src/sys/arch/arm/allwinner/awin_var.h:1.38
--- src/sys/arch/arm/allwinner/awin_var.h:1.37	Sun Oct 25 20:54:19 2015
+++ src/sys/arch/arm/allwinner/awin_var.h	Sun Nov 15 21:28:54 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_var.h,v 1.37 2015/10/25 20:54:19 bouyer Exp $ */
+/* $NetBSD: awin_var.h,v 1.38 2015/11/15 21:28:54 bouyer Exp $ */
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -140,8 +140,8 @@ struct videomode;
 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_tcon1_set_videomode(int, const struct videomode *);
+void	awin_tcon1_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 *);

Index: src/sys/arch/evbarm/awin/awin_machdep.c
diff -u src/sys/arch/evbarm/awin/awin_machdep.c:1.46 src/sys/arch/evbarm/awin/awin_machdep.c:1.47
--- src/sys/arch/evbarm/awin/awin_machdep.c:1.46	Tue Nov  3 18:38:03 2015
+++ src/sys/arch/evbarm/awin/awin_machdep.c	Sun Nov 15 21:28:54 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: awin_machdep.c,v 1.46 2015/11/03 18:38:03 bouyer Exp $ */
+/*	$NetBSD: awin_machdep.c,v 1.47 2015/11/15 21:28:54 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.46 2015/11/03 18:38:03 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_machdep.c,v 1.47 2015/11/15 21:28:54 bouyer Exp $");
 
 #include "opt_machdep.h"
 #include "opt_ddb.h"
@@ -941,6 +941,10 @@ awin_gpio_sysconfig(prop_dictionary_t di
 		{ "mmc0detect",		"mmc0_para", "sdc_det" },
 		{ "audiopactrl",	"audio_para", "audio_pa_ctrl" },
 		{ "gmacpwren",		"gmac_phy_power", "gmac_phy_power_en" },
+		{ "lcd0_power_en",	"lcd0_para", "lcd_power" },
+		{ "lcd0_bl_en",		"lcd0_para", "lcd_bl_en" },
+		{ "lcd1_power_en",	"lcd1_para", "lcd_power" },
+		{ "lcd1_bl_en",		"lcd1_para", "lcd_bl_en" },
 	};
 	unsigned int n;
 
@@ -1057,6 +1061,16 @@ awin_tcon_sysconfig(device_t self, prop_
 		if (type == 1) {
 			/* LCD/LVDS output */
 			awin_tcon_lcd_sysconfig("lcd0_para", dict);
+			if (awin_sysconfig_get_int("lcd0_para",
+			    "lcd_bl_en_used") == 1) {
+				prop_dictionary_set_cstring(dict,
+				    "lcd_bl_en", "lcd0_bl_en");
+			}
+			if (awin_sysconfig_get_int("lcd0_para",
+			    "lcd_power_used") == 1) {
+				prop_dictionary_set_cstring(dict,
+				    "lcd_power_en", "lcd0_power_en");
+			}
 			return;
 		}
 		if (type == 3) {
@@ -1071,6 +1085,16 @@ awin_tcon_sysconfig(device_t self, prop_
 		if (type == 1) {
 			/* LCD/LVDS output */
 			awin_tcon_lcd_sysconfig("lcd1_para", dict);
+			if (awin_sysconfig_get_int("lcd1_para",
+			    "lcd_bl_en_used") == 1) {
+				prop_dictionary_set_cstring(dict,
+				    "lcd_bl_en", "lcd1_bl_en");
+			}
+			if (awin_sysconfig_get_int("lcd1_para",
+			    "lcd_power_used") == 1) {
+				prop_dictionary_set_cstring(dict,
+				    "lcd_power_en", "lcd1_power_en");
+			}
 			return;
 		}
 		if (type == 3) {
@@ -1091,9 +1115,10 @@ awin_tcon_lcd_sysconfig(const char *key,
 		"lcd_dclk_freq",
 		"lcd_hbp",
 		"lcd_ht",
+		"lcd_hspw",
 		"lcd_vbp",
 		"lcd_vt",
-		"lcd_hv_hspw",
+		"lcd_vspw",
 		"lcd_io_cfg0",
 	};
 	static const char *lcdgpio[] = {
@@ -1129,24 +1154,39 @@ awin_tcon_lcd_sysconfig(const char *key,
 	unsigned int n;
 	const char *cfg;
 
+	switch(awin_sysconfig_get_int(key, "lcd_if")) {
+	case -1:
+		/* error */
+		return;
+	case 3:
+		prop_dictionary_set_cstring(dict, "output", "lvds");
+
+		if (awin_sysconfig_get_int(key, "lcd_lvds_ch") == 1)
+			prop_dictionary_set_bool(dict, "lvds_dual", true);
+		else
+			prop_dictionary_set_bool(dict, "lvds_dual", false);
+
+		if (awin_sysconfig_get_int(key, "lcd_lvds_mode") == 1)
+			prop_dictionary_set_bool(dict, "lvds_mode_jeida", true);
+		else
+			prop_dictionary_set_bool(dict, "lvds_mode_jeida", false);
+
+		if (awin_sysconfig_get_int(key, "lcd_lvds_bitwidth") == 1)
+			prop_dictionary_set_bool(dict, "lvds_18bits", true);
+		else
+			prop_dictionary_set_bool(dict, "lvds_18bits", false);
+		break;
+	default:
+		/* unsupported */
+		return;
+	}
+
 	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) {

Reply via email to