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) {