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 <[email protected]>
@@ -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 <[email protected]>
@@ -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) {