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