Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/sunxi Add TCON0 support



details:   https://anonhg.NetBSD.org/src/rev/18460f38de58
branches:  trunk
changeset: 448462:18460f38de58
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sun Feb 03 13:15:19 2019 +0000

description:
Add TCON0 support

diffstat:

 sys/arch/arm/sunxi/sunxi_lcdc.c |  191 ++++++++++++++++++++++++++++++++++-----
 1 files changed, 163 insertions(+), 28 deletions(-)

diffs (truncated from 325 to 300 lines):

diff -r a0fd84ffeeac -r 18460f38de58 sys/arch/arm/sunxi/sunxi_lcdc.c
--- a/sys/arch/arm/sunxi/sunxi_lcdc.c   Sun Feb 03 13:11:07 2019 +0000
+++ b/sys/arch/arm/sunxi/sunxi_lcdc.c   Sun Feb 03 13:15:19 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_lcdc.c,v 1.2 2019/01/31 01:49:28 jmcneill Exp $ */
+/* $NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.2 2019/01/31 01:49:28 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -47,11 +47,31 @@
 #define         TCON_GCTL_TCON_EN                      __BIT(31)
 #define         TCON_GCTL_GAMMA_EN                     __BIT(30)
 #define         TCON_GCTL_IO_MAP_SEL                   __BIT(0)
-
 #define        TCON_GINT0_REG          0x004
 #define        TCON_GINT1_REG          0x008
 #define         TCON_GINT1_TCON1_LINE_INT_NUM          __BITS(11,0)
 
+#define        TCON0_CTL_REG           0x040
+#define         TCON0_CTL_TCON0_EN                     __BIT(31)
+#define         TCON0_CTL_START_DELAY                  __BITS(8,4)
+#define         TCON0_CTL_TCON0_SRC_SEL                __BITS(2,0)
+#define        TCON0_DCLK_REG          0x044
+#define         TCON0_DCLK_EN                          __BITS(31,28)
+#define         TCON0_DCLK_DIV                         __BITS(6,0)
+#define        TCON0_BASIC0_REG        0x048
+#define        TCON0_BASIC1_REG        0x04c
+#define        TCON0_BASIC2_REG        0x050
+#define        TCON0_BASIC3_REG        0x054
+#define        TCON0_IO_POL_REG        0x088
+#define         TCON0_IO_POL_IO_OUTPUT_SEL             __BIT(31)
+#define         TCON0_IO_POL_DCLK_SEL                  __BITS(30,28)
+#define         TCON0_IO_POL_IO3_INV                   __BIT(27)
+#define         TCON0_IO_POL_IO2_INV                   __BIT(26)
+#define         TCON0_IO_POL_IO1_INV                   __BIT(25)
+#define         TCON0_IO_POL_IO0_INV                   __BIT(24)
+#define         TCON0_IO_POL_DATA_INV                  __BITS(23,0)
+#define        TCON0_IO_TRI_REG        0x08c
+
 #define        TCON1_CTL_REG           0x090
 #define         TCON1_CTL_TCON1_EN                     __BIT(31)
 #define         TCON1_CTL_START_DELAY                  __BITS(8,4)
@@ -62,7 +82,6 @@
 #define        TCON1_BASIC3_REG        0x0a0
 #define        TCON1_BASIC4_REG        0x0a4
 #define        TCON1_BASIC5_REG        0x0a8
-
 #define        TCON1_IO_POL_REG        0x0f0
 #define         TCON1_IO_POL_IO3_INV                   __BIT(27)
 #define         TCON1_IO_POL_IO2_INV                   __BIT(26)
@@ -72,15 +91,20 @@
 #define        TCON1_IO_TRI_REG        0x0f4
 
 enum {
-       MIXER_PORT_INPUT = 0,
-       MIXER_PORT_OUTPUT = 1,
+       TCON_PORT_INPUT = 0,
+       TCON_PORT_OUTPUT = 1,
 };
 
-static const char * const compatible[] = {
-       "allwinner,sun8i-h3-tcon-tv",
-       "allwinner,sun50i-a64-tcon-lcd",
-       "allwinner,sun50i-a64-tcon-tv",
-       NULL
+enum tcon_type {
+       TYPE_TCON0,
+       TYPE_TCON1,
+};
+
+static const struct of_compat_data compat_data[] = {
+       { "allwinner,sun8i-h3-tcon-tv",         TYPE_TCON1 },
+       { "allwinner,sun50i-a64-tcon-lcd",      TYPE_TCON0 },
+       { "allwinner,sun50i-a64-tcon-tv",       TYPE_TCON1 },
+       { NULL }
 };
 
 struct sunxi_lcdc_softc;
@@ -97,6 +121,8 @@
        bus_space_handle_t      sc_bsh;
        int                     sc_phandle;
 
+       enum tcon_type          sc_type;
+
        struct clk              *sc_clk_ch[2];
 
        struct sunxi_lcdc_encoder sc_encoder;
@@ -122,19 +148,19 @@
 };
 
 static void
-sunxi_lcdc_tcon1_dpms(struct drm_encoder *encoder, int mode)
+sunxi_lcdc_tcon_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
 static bool
-sunxi_lcdc_tcon1_mode_fixup(struct drm_encoder *encoder,
+sunxi_lcdc_tcon_mode_fixup(struct drm_encoder *encoder,
     const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
 {
        return true;
 }
 
 static void
-sunxi_lcdc_tcon1_mode_set(struct drm_encoder *encoder,
+sunxi_lcdc_tcon_mode_set(struct drm_encoder *encoder,
     struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
 {
        struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
@@ -143,6 +169,20 @@
 }
 
 static void
+sunxi_lcdc_tcon0_prepare(struct drm_encoder *encoder)
+{
+       struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
+       struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
+       uint32_t val;
+
+       val = TCON_READ(sc, TCON_GCTL_REG);
+       val |= TCON_GCTL_TCON_EN;
+       TCON_WRITE(sc, TCON_GCTL_REG, val);
+
+       TCON_WRITE(sc, TCON0_IO_TRI_REG, 0);
+}
+
+static void
 sunxi_lcdc_tcon1_prepare(struct drm_encoder *encoder)
 {
        struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
@@ -158,6 +198,61 @@
 }
 
 static void
+sunxi_lcdc_tcon0_commit(struct drm_encoder *encoder)
+{
+       struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
+       struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
+       struct drm_display_mode *mode = &lcdc_encoder->curmode;
+       uint32_t val;
+       int error;
+
+       const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0;
+       const u_int hspw = mode->hsync_end - mode->hsync_start;
+       const u_int hbp = mode->htotal - mode->hsync_start;
+       const u_int vspw = mode->vsync_end - mode->vsync_start;
+       const u_int vbp = mode->vtotal - mode->vsync_start;
+       const u_int vblank_len =
+           ((mode->vtotal << interlace_p) >> 1) - mode->vdisplay - 2;
+       const u_int start_delay =
+           vblank_len >= 32 ? 30 : vblank_len - 2;
+
+       val = TCON0_CTL_TCON0_EN |
+             __SHIFTIN(start_delay, TCON0_CTL_START_DELAY);
+       TCON_WRITE(sc, TCON0_CTL_REG, val);
+
+       TCON_WRITE(sc, TCON0_BASIC0_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+       TCON_WRITE(sc, TCON0_BASIC1_REG, ((mode->htotal - 1) << 16) | (hbp - 1));
+       TCON_WRITE(sc, TCON0_BASIC2_REG, ((mode->vtotal * 2) << 16) | (vbp - 1));
+       TCON_WRITE(sc, TCON0_BASIC3_REG, ((hspw - 1) << 16) | (vspw - 1));
+
+       val = TCON_READ(sc, TCON0_IO_POL_REG);
+       val &= ~(TCON0_IO_POL_IO3_INV|TCON0_IO_POL_IO2_INV|
+                TCON0_IO_POL_IO1_INV|TCON0_IO_POL_IO0_INV|
+                TCON0_IO_POL_DATA_INV);
+       if ((mode->flags & DRM_MODE_FLAG_PHSYNC) == 0)
+               val |= TCON0_IO_POL_IO1_INV;
+       if ((mode->flags & DRM_MODE_FLAG_PVSYNC) == 0)
+               val |= TCON0_IO_POL_IO0_INV;
+       TCON_WRITE(sc, TCON0_IO_POL_REG, val);
+
+       if (sc->sc_clk_ch[0] != NULL) {
+               error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "failed to set CH0 PLL rate to %u Hz: %d\n",
+                           mode->crtc_clock * 1000, error);
+                       return;
+               }
+               error = clk_enable(sc->sc_clk_ch[1]);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "failed to enable CH0 PLL: %d\n", error);
+                       return;
+               }
+       } else {
+               device_printf(sc->sc_dev, "no CH0 PLL configured\n");
+       }
+}
+
+static void
 sunxi_lcdc_tcon1_commit(struct drm_encoder *encoder)
 {
        struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
@@ -207,27 +302,52 @@
        }
 }
 
+static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon0_helper_funcs = {
+       .dpms = sunxi_lcdc_tcon_dpms,
+       .mode_fixup = sunxi_lcdc_tcon_mode_fixup,
+       .prepare = sunxi_lcdc_tcon0_prepare,
+       .commit = sunxi_lcdc_tcon0_commit,
+       .mode_set = sunxi_lcdc_tcon_mode_set,
+};
+
 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon1_helper_funcs = {
-       .dpms = sunxi_lcdc_tcon1_dpms,
-       .mode_fixup = sunxi_lcdc_tcon1_mode_fixup,
+       .dpms = sunxi_lcdc_tcon_dpms,
+       .mode_fixup = sunxi_lcdc_tcon_mode_fixup,
        .prepare = sunxi_lcdc_tcon1_prepare,
        .commit = sunxi_lcdc_tcon1_commit,
-       .mode_set = sunxi_lcdc_tcon1_mode_set,
+       .mode_set = sunxi_lcdc_tcon_mode_set,
 };
 
 static int
+sunxi_lcdc_encoder_mode(struct fdt_endpoint *out_ep)
+{
+       struct fdt_endpoint *remote_ep = fdt_endpoint_remote(out_ep);
+
+       if (remote_ep == NULL)
+               return DRM_MODE_ENCODER_NONE;
+
+       switch (fdt_endpoint_type(remote_ep)) {
+       case EP_DRM_BRIDGE:
+               return DRM_MODE_ENCODER_TMDS;
+       case EP_DRM_PANEL:
+               return DRM_MODE_ENCODER_LVDS;
+       default:
+               return DRM_MODE_ENCODER_NONE;
+       }
+}
+
+static int
 sunxi_lcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
 {
        struct sunxi_lcdc_softc * const sc = device_private(dev);
        struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep);
        struct fdt_endpoint *out_ep;
        struct drm_crtc *crtc;
-       int error;
 
        if (!activate)
                return EINVAL;
 
-       if (fdt_endpoint_port_index(ep) != MIXER_PORT_INPUT)
+       if (fdt_endpoint_port_index(ep) != TCON_PORT_INPUT)
                return EINVAL;
 
        if (fdt_endpoint_type(in_ep) != EP_DRM_CRTC)
@@ -236,20 +356,27 @@
        crtc = fdt_endpoint_get_data(in_ep);
 
        sc->sc_encoder.sc = sc;
+       sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(crtc);
 
-       out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, MIXER_PORT_OUTPUT, 1);
+       out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 0);
        if (out_ep != NULL) {
                drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs,
-                   DRM_MODE_ENCODER_TMDS);
+                   sunxi_lcdc_encoder_mode(out_ep));
+               drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon0_helper_funcs);
+
+               return fdt_endpoint_activate(out_ep, activate);
+       }
+
+       out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 1);
+       if (out_ep != NULL) {
+               drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs,
+                   sunxi_lcdc_encoder_mode(out_ep));
                drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon1_helper_funcs);
 
-               error = fdt_endpoint_activate(out_ep, activate);
-               if (error != 0)
-                       return error;
-               sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(crtc);
+               return fdt_endpoint_activate(out_ep, activate);
        }
 
-       return 0;
+       return ENXIO;
 }
 
 static void *
@@ -265,7 +392,7 @@
 {
        struct fdt_attach_args * const faa = aux;
 
-       return of_match_compatible(faa->faa_phandle, compatible);



Home | Main Index | Thread Index | Old Index