Home | History | Annotate | Line # | Download | only in sunxi
sunxi_lcdc.c revision 1.3
      1 /* $NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.3 2019/02/03 13:15:19 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/device.h>
     35 #include <sys/intr.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 #include <sys/conf.h>
     39 
     40 #include <drm/drmP.h>
     41 #include <drm/drm_crtc_helper.h>
     42 
     43 #include <dev/fdt/fdtvar.h>
     44 #include <dev/fdt/fdt_port.h>
     45 
     46 #define	TCON_GCTL_REG		0x000
     47 #define	 TCON_GCTL_TCON_EN			__BIT(31)
     48 #define	 TCON_GCTL_GAMMA_EN			__BIT(30)
     49 #define	 TCON_GCTL_IO_MAP_SEL			__BIT(0)
     50 #define	TCON_GINT0_REG		0x004
     51 #define	TCON_GINT1_REG		0x008
     52 #define	 TCON_GINT1_TCON1_LINE_INT_NUM		__BITS(11,0)
     53 
     54 #define	TCON0_CTL_REG		0x040
     55 #define	 TCON0_CTL_TCON0_EN			__BIT(31)
     56 #define	 TCON0_CTL_START_DELAY			__BITS(8,4)
     57 #define	 TCON0_CTL_TCON0_SRC_SEL		__BITS(2,0)
     58 #define	TCON0_DCLK_REG		0x044
     59 #define	 TCON0_DCLK_EN				__BITS(31,28)
     60 #define	 TCON0_DCLK_DIV				__BITS(6,0)
     61 #define	TCON0_BASIC0_REG	0x048
     62 #define	TCON0_BASIC1_REG	0x04c
     63 #define	TCON0_BASIC2_REG	0x050
     64 #define	TCON0_BASIC3_REG	0x054
     65 #define	TCON0_IO_POL_REG	0x088
     66 #define	 TCON0_IO_POL_IO_OUTPUT_SEL		__BIT(31)
     67 #define	 TCON0_IO_POL_DCLK_SEL			__BITS(30,28)
     68 #define	 TCON0_IO_POL_IO3_INV			__BIT(27)
     69 #define	 TCON0_IO_POL_IO2_INV			__BIT(26)
     70 #define	 TCON0_IO_POL_IO1_INV			__BIT(25)
     71 #define	 TCON0_IO_POL_IO0_INV			__BIT(24)
     72 #define	 TCON0_IO_POL_DATA_INV			__BITS(23,0)
     73 #define	TCON0_IO_TRI_REG	0x08c
     74 
     75 #define	TCON1_CTL_REG		0x090
     76 #define	 TCON1_CTL_TCON1_EN			__BIT(31)
     77 #define	 TCON1_CTL_START_DELAY			__BITS(8,4)
     78 #define	 TCON1_CTL_TCON1_SRC_SEL		__BIT(1)
     79 #define	TCON1_BASIC0_REG	0x094
     80 #define	TCON1_BASIC1_REG	0x098
     81 #define	TCON1_BASIC2_REG	0x09c
     82 #define	TCON1_BASIC3_REG	0x0a0
     83 #define	TCON1_BASIC4_REG	0x0a4
     84 #define	TCON1_BASIC5_REG	0x0a8
     85 #define	TCON1_IO_POL_REG	0x0f0
     86 #define	 TCON1_IO_POL_IO3_INV			__BIT(27)
     87 #define	 TCON1_IO_POL_IO2_INV			__BIT(26)
     88 #define	 TCON1_IO_POL_IO1_INV			__BIT(25)
     89 #define	 TCON1_IO_POL_IO0_INV			__BIT(24)
     90 #define	 TCON1_IO_POL_DATA_INV			__BITS(23,0)
     91 #define	TCON1_IO_TRI_REG	0x0f4
     92 
     93 enum {
     94 	TCON_PORT_INPUT = 0,
     95 	TCON_PORT_OUTPUT = 1,
     96 };
     97 
     98 enum tcon_type {
     99 	TYPE_TCON0,
    100 	TYPE_TCON1,
    101 };
    102 
    103 static const struct of_compat_data compat_data[] = {
    104 	{ "allwinner,sun8i-h3-tcon-tv",		TYPE_TCON1 },
    105 	{ "allwinner,sun50i-a64-tcon-lcd",	TYPE_TCON0 },
    106 	{ "allwinner,sun50i-a64-tcon-tv",	TYPE_TCON1 },
    107 	{ NULL }
    108 };
    109 
    110 struct sunxi_lcdc_softc;
    111 
    112 struct sunxi_lcdc_encoder {
    113 	struct drm_encoder	base;
    114 	struct sunxi_lcdc_softc *sc;
    115 	struct drm_display_mode	curmode;
    116 };
    117 
    118 struct sunxi_lcdc_softc {
    119 	device_t		sc_dev;
    120 	bus_space_tag_t		sc_bst;
    121 	bus_space_handle_t	sc_bsh;
    122 	int			sc_phandle;
    123 
    124 	enum tcon_type		sc_type;
    125 
    126 	struct clk		*sc_clk_ch[2];
    127 
    128 	struct sunxi_lcdc_encoder sc_encoder;
    129 	struct drm_connector	sc_connector;
    130 
    131 	struct fdt_device_ports	sc_ports;
    132 };
    133 
    134 #define	to_sunxi_lcdc_encoder(x)	container_of(x, struct sunxi_lcdc_encoder, base)
    135 
    136 #define	TCON_READ(sc, reg)				\
    137 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
    138 #define	TCON_WRITE(sc, reg, val)			\
    139 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
    140 
    141 static void
    142 sunxi_lcdc_destroy(struct drm_encoder *encoder)
    143 {
    144 }
    145 
    146 static const struct drm_encoder_funcs sunxi_lcdc_funcs = {
    147 	.destroy = sunxi_lcdc_destroy,
    148 };
    149 
    150 static void
    151 sunxi_lcdc_tcon_dpms(struct drm_encoder *encoder, int mode)
    152 {
    153 }
    154 
    155 static bool
    156 sunxi_lcdc_tcon_mode_fixup(struct drm_encoder *encoder,
    157     const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
    158 {
    159 	return true;
    160 }
    161 
    162 static void
    163 sunxi_lcdc_tcon_mode_set(struct drm_encoder *encoder,
    164     struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
    165 {
    166 	struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
    167 
    168 	lcdc_encoder->curmode = *adjusted_mode;
    169 }
    170 
    171 static void
    172 sunxi_lcdc_tcon0_prepare(struct drm_encoder *encoder)
    173 {
    174 	struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
    175 	struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
    176 	uint32_t val;
    177 
    178 	val = TCON_READ(sc, TCON_GCTL_REG);
    179 	val |= TCON_GCTL_TCON_EN;
    180 	TCON_WRITE(sc, TCON_GCTL_REG, val);
    181 
    182 	TCON_WRITE(sc, TCON0_IO_TRI_REG, 0);
    183 }
    184 
    185 static void
    186 sunxi_lcdc_tcon1_prepare(struct drm_encoder *encoder)
    187 {
    188 	struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
    189 	struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
    190 	uint32_t val;
    191 
    192 	val = TCON_READ(sc, TCON_GCTL_REG);
    193 	val |= TCON_GCTL_TCON_EN;
    194 	TCON_WRITE(sc, TCON_GCTL_REG, val);
    195 
    196 	TCON_WRITE(sc, TCON1_IO_POL_REG, 0);
    197 	TCON_WRITE(sc, TCON1_IO_TRI_REG, 0xffffffff);
    198 }
    199 
    200 static void
    201 sunxi_lcdc_tcon0_commit(struct drm_encoder *encoder)
    202 {
    203 	struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
    204 	struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
    205 	struct drm_display_mode *mode = &lcdc_encoder->curmode;
    206 	uint32_t val;
    207 	int error;
    208 
    209 	const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0;
    210 	const u_int hspw = mode->hsync_end - mode->hsync_start;
    211 	const u_int hbp = mode->htotal - mode->hsync_start;
    212 	const u_int vspw = mode->vsync_end - mode->vsync_start;
    213 	const u_int vbp = mode->vtotal - mode->vsync_start;
    214 	const u_int vblank_len =
    215 	    ((mode->vtotal << interlace_p) >> 1) - mode->vdisplay - 2;
    216 	const u_int start_delay =
    217 	    vblank_len >= 32 ? 30 : vblank_len - 2;
    218 
    219 	val = TCON0_CTL_TCON0_EN |
    220 	      __SHIFTIN(start_delay, TCON0_CTL_START_DELAY);
    221 	TCON_WRITE(sc, TCON0_CTL_REG, val);
    222 
    223 	TCON_WRITE(sc, TCON0_BASIC0_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
    224 	TCON_WRITE(sc, TCON0_BASIC1_REG, ((mode->htotal - 1) << 16) | (hbp - 1));
    225 	TCON_WRITE(sc, TCON0_BASIC2_REG, ((mode->vtotal * 2) << 16) | (vbp - 1));
    226 	TCON_WRITE(sc, TCON0_BASIC3_REG, ((hspw - 1) << 16) | (vspw - 1));
    227 
    228 	val = TCON_READ(sc, TCON0_IO_POL_REG);
    229 	val &= ~(TCON0_IO_POL_IO3_INV|TCON0_IO_POL_IO2_INV|
    230 		 TCON0_IO_POL_IO1_INV|TCON0_IO_POL_IO0_INV|
    231 		 TCON0_IO_POL_DATA_INV);
    232 	if ((mode->flags & DRM_MODE_FLAG_PHSYNC) == 0)
    233 		val |= TCON0_IO_POL_IO1_INV;
    234 	if ((mode->flags & DRM_MODE_FLAG_PVSYNC) == 0)
    235 		val |= TCON0_IO_POL_IO0_INV;
    236 	TCON_WRITE(sc, TCON0_IO_POL_REG, val);
    237 
    238 	if (sc->sc_clk_ch[0] != NULL) {
    239 		error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000);
    240 		if (error != 0) {
    241 			device_printf(sc->sc_dev, "failed to set CH0 PLL rate to %u Hz: %d\n",
    242 			    mode->crtc_clock * 1000, error);
    243 			return;
    244 		}
    245 		error = clk_enable(sc->sc_clk_ch[1]);
    246 		if (error != 0) {
    247 			device_printf(sc->sc_dev, "failed to enable CH0 PLL: %d\n", error);
    248 			return;
    249 		}
    250 	} else {
    251 		device_printf(sc->sc_dev, "no CH0 PLL configured\n");
    252 	}
    253 }
    254 
    255 static void
    256 sunxi_lcdc_tcon1_commit(struct drm_encoder *encoder)
    257 {
    258 	struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder);
    259 	struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc;
    260 	struct drm_display_mode *mode = &lcdc_encoder->curmode;
    261 	uint32_t val;
    262 	int error;
    263 
    264 	const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0;
    265 	const u_int hspw = mode->hsync_end - mode->hsync_start;
    266 	const u_int hbp = mode->htotal - mode->hsync_start;
    267 	const u_int vspw = mode->vsync_end - mode->vsync_start;
    268 	const u_int vbp = mode->vtotal - mode->vsync_start;
    269 	const u_int vblank_len =
    270 	    ((mode->vtotal << interlace_p) >> 1) - mode->vdisplay - 2;
    271 	const u_int start_delay =
    272 	    vblank_len >= 32 ? 30 : vblank_len - 2;
    273 
    274 	val = TCON1_CTL_TCON1_EN |
    275 	      __SHIFTIN(start_delay, TCON1_CTL_START_DELAY);
    276 	TCON_WRITE(sc, TCON1_CTL_REG, val);
    277 
    278 	TCON_WRITE(sc, TCON1_BASIC0_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
    279 	TCON_WRITE(sc, TCON1_BASIC1_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
    280 	TCON_WRITE(sc, TCON1_BASIC2_REG, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
    281 	TCON_WRITE(sc, TCON1_BASIC3_REG, ((mode->htotal - 1) << 16) | (hbp - 1));
    282 	TCON_WRITE(sc, TCON1_BASIC4_REG, ((mode->vtotal * 2) << 16) | (vbp - 1));
    283 	TCON_WRITE(sc, TCON1_BASIC5_REG, ((hspw - 1) << 16) | (vspw - 1));
    284 
    285 	TCON_WRITE(sc, TCON_GINT1_REG,
    286 	    __SHIFTIN(start_delay + 2, TCON_GINT1_TCON1_LINE_INT_NUM));
    287 
    288 	if (sc->sc_clk_ch[1] != NULL) {
    289 		error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000);
    290 		if (error != 0) {
    291 			device_printf(sc->sc_dev, "failed to set CH1 PLL rate to %u Hz: %d\n",
    292 			    mode->crtc_clock * 1000, error);
    293 			return;
    294 		}
    295 		error = clk_enable(sc->sc_clk_ch[1]);
    296 		if (error != 0) {
    297 			device_printf(sc->sc_dev, "failed to enable CH1 PLL: %d\n", error);
    298 			return;
    299 		}
    300 	} else {
    301 		device_printf(sc->sc_dev, "no CH1 PLL configured\n");
    302 	}
    303 }
    304 
    305 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon0_helper_funcs = {
    306 	.dpms = sunxi_lcdc_tcon_dpms,
    307 	.mode_fixup = sunxi_lcdc_tcon_mode_fixup,
    308 	.prepare = sunxi_lcdc_tcon0_prepare,
    309 	.commit = sunxi_lcdc_tcon0_commit,
    310 	.mode_set = sunxi_lcdc_tcon_mode_set,
    311 };
    312 
    313 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon1_helper_funcs = {
    314 	.dpms = sunxi_lcdc_tcon_dpms,
    315 	.mode_fixup = sunxi_lcdc_tcon_mode_fixup,
    316 	.prepare = sunxi_lcdc_tcon1_prepare,
    317 	.commit = sunxi_lcdc_tcon1_commit,
    318 	.mode_set = sunxi_lcdc_tcon_mode_set,
    319 };
    320 
    321 static int
    322 sunxi_lcdc_encoder_mode(struct fdt_endpoint *out_ep)
    323 {
    324 	struct fdt_endpoint *remote_ep = fdt_endpoint_remote(out_ep);
    325 
    326 	if (remote_ep == NULL)
    327 		return DRM_MODE_ENCODER_NONE;
    328 
    329 	switch (fdt_endpoint_type(remote_ep)) {
    330 	case EP_DRM_BRIDGE:
    331 		return DRM_MODE_ENCODER_TMDS;
    332 	case EP_DRM_PANEL:
    333 		return DRM_MODE_ENCODER_LVDS;
    334 	default:
    335 		return DRM_MODE_ENCODER_NONE;
    336 	}
    337 }
    338 
    339 static int
    340 sunxi_lcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
    341 {
    342 	struct sunxi_lcdc_softc * const sc = device_private(dev);
    343 	struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep);
    344 	struct fdt_endpoint *out_ep;
    345 	struct drm_crtc *crtc;
    346 
    347 	if (!activate)
    348 		return EINVAL;
    349 
    350 	if (fdt_endpoint_port_index(ep) != TCON_PORT_INPUT)
    351 		return EINVAL;
    352 
    353 	if (fdt_endpoint_type(in_ep) != EP_DRM_CRTC)
    354 		return EINVAL;
    355 
    356 	crtc = fdt_endpoint_get_data(in_ep);
    357 
    358 	sc->sc_encoder.sc = sc;
    359 	sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(crtc);
    360 
    361 	out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 0);
    362 	if (out_ep != NULL) {
    363 		drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs,
    364 		    sunxi_lcdc_encoder_mode(out_ep));
    365 		drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon0_helper_funcs);
    366 
    367 		return fdt_endpoint_activate(out_ep, activate);
    368 	}
    369 
    370 	out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 1);
    371 	if (out_ep != NULL) {
    372 		drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs,
    373 		    sunxi_lcdc_encoder_mode(out_ep));
    374 		drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon1_helper_funcs);
    375 
    376 		return fdt_endpoint_activate(out_ep, activate);
    377 	}
    378 
    379 	return ENXIO;
    380 }
    381 
    382 static void *
    383 sunxi_lcdc_ep_get_data(device_t dev, struct fdt_endpoint *ep)
    384 {
    385 	struct sunxi_lcdc_softc * const sc = device_private(dev);
    386 
    387 	return &sc->sc_encoder;
    388 }
    389 
    390 static int
    391 sunxi_lcdc_match(device_t parent, cfdata_t cf, void *aux)
    392 {
    393 	struct fdt_attach_args * const faa = aux;
    394 
    395 	return of_match_compat_data(faa->faa_phandle, compat_data);
    396 }
    397 
    398 static void
    399 sunxi_lcdc_attach(device_t parent, device_t self, void *aux)
    400 {
    401 	struct sunxi_lcdc_softc * const sc = device_private(self);
    402 	struct fdt_attach_args * const faa = aux;
    403 	const int phandle = faa->faa_phandle;
    404 	struct fdtbus_reset *rst;
    405 	struct clk *clk;
    406 	bus_addr_t addr;
    407 	bus_size_t size;
    408 
    409 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    410 		aprint_error(": couldn't get registers\n");
    411 		return;
    412 	}
    413 
    414 	rst = fdtbus_reset_get(phandle, "lcd");
    415 	if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
    416 		aprint_error(": couldn't de-assert reset\n");
    417 		return;
    418 	}
    419 
    420 	clk = fdtbus_clock_get(phandle, "ahb");
    421 	if (clk == NULL || clk_enable(clk) != 0) {
    422 		aprint_error(": couldn't enable bus clock\n");
    423 		return;
    424 	}
    425 
    426 	sc->sc_dev = self;
    427 	sc->sc_bst = faa->faa_bst;
    428 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    429 		aprint_error(": couldn't map registers\n");
    430 		return;
    431 	}
    432 	sc->sc_phandle = faa->faa_phandle;
    433 	sc->sc_type = of_search_compatible(phandle, compat_data)->data;
    434 	sc->sc_clk_ch[0] = fdtbus_clock_get(phandle, "tcon-ch0");
    435 	sc->sc_clk_ch[1] = fdtbus_clock_get(phandle, "tcon-ch1");
    436 
    437 	aprint_naive("\n");
    438 	switch (sc->sc_type) {
    439 	case TYPE_TCON0:
    440 		aprint_normal(": TCON0\n");
    441 		break;
    442 	case TYPE_TCON1:
    443 		aprint_normal(": TCON1\n");
    444 		break;
    445 	}
    446 
    447 	sc->sc_ports.dp_ep_activate = sunxi_lcdc_ep_activate;
    448 	sc->sc_ports.dp_ep_get_data = sunxi_lcdc_ep_get_data;
    449 	fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER);
    450 }
    451 
    452 CFATTACH_DECL_NEW(sunxi_lcdc, sizeof(struct sunxi_lcdc_softc),
    453 	sunxi_lcdc_match, sunxi_lcdc_attach, NULL, NULL);
    454