Module Name:    src
Committed By:   jmcneill
Date:           Sun Nov  3 22:59:06 UTC 2019

Modified Files:
        src/sys/arch/arm/ti: am3_prcm.c files.ti
Added Files:
        src/sys/arch/arm/ti: ti_fb.c ti_lcdc.c ti_lcdc.h ti_lcdcreg.h

Log Message:
Add support for AM335x display controller (LCDC).


To generate a diff of this commit:
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/arm/ti/am3_prcm.c
cvs rdiff -u -r1.19 -r1.20 src/sys/arch/arm/ti/files.ti
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/ti/ti_fb.c \
    src/sys/arch/arm/ti/ti_lcdc.c src/sys/arch/arm/ti/ti_lcdc.h \
    src/sys/arch/arm/ti/ti_lcdcreg.h

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/ti/am3_prcm.c
diff -u src/sys/arch/arm/ti/am3_prcm.c:1.8 src/sys/arch/arm/ti/am3_prcm.c:1.9
--- src/sys/arch/arm/ti/am3_prcm.c:1.8	Wed Oct 30 21:40:04 2019
+++ src/sys/arch/arm/ti/am3_prcm.c	Sun Nov  3 22:59:06 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: am3_prcm.c,v 1.8 2019/10/30 21:40:04 jmcneill Exp $ */
+/* $NetBSD: am3_prcm.c,v 1.9 2019/11/03 22:59:06 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca>
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: am3_prcm.c,v 1.8 2019/10/30 21:40:04 jmcneill Exp $");
+__KERNEL_RCSID(1, "$NetBSD: am3_prcm.c,v 1.9 2019/11/03 22:59:06 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -52,6 +52,18 @@ __KERNEL_RCSID(1, "$NetBSD: am3_prcm.c,v
 #define	AM3_PRCM_CLKCTRL_MODULEMODE		__BITS(1,0)
 #define	AM3_PRCM_CLKCTRL_MODULEMODE_ENABLE	0x2
 
+/* WKUP */
+#define	AM3_PRCM_CM_IDLEST_DPLL_DISP	(AM3_PRCM_CM_WKUP + 0x48)
+#define	 AM3_PRCM_CM_IDLEST_DPLL_DISP_ST_MN_BYPASS	__BIT(8)
+#define	 AM3_PRCM_CM_IDLEST_DPLL_DISP_ST_DPLL_CLK	__BIT(0)
+#define	AM3_PRCM_CM_CLKSEL_DPLL_DISP	(AM3_PRCM_CM_WKUP + 0x54)
+#define	 AM3_PRCM_CM_CLKSEL_DPLL_DISP_DPLL_MULT		__BITS(18,8)
+#define	 AM3_PRCM_CM_CLKSEL_DPLL_DISP_DPLL_DIV		__BITS(6,0)
+#define	AM3_PRCM_CM_CLKMODE_DPLL_DISP	(AM3_PRCM_CM_WKUP + 0x98)
+#define	 AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN		__BITS(2,0)
+#define	  AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN_MN_BYPASS	4
+#define	  AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN_LOCK		7
+
 static int am3_prcm_match(device_t, cfdata_t, void *);
 static void am3_prcm_attach(device_t, device_t, void *);
 
@@ -70,8 +82,48 @@ am3_prcm_hwmod_enable(struct ti_prcm_sof
 	return 0;
 }
 
+static int
+am3_prcm_hwmod_enable_display(struct ti_prcm_softc *sc, struct ti_prcm_clk *tc, int enable)
+{
+	uint32_t val;
+	int retry;
+
+	if (enable) {
+		/* Put the DPLL in MN bypass mode */
+		PRCM_WRITE(sc, AM3_PRCM_CM_CLKMODE_DPLL_DISP,
+		    __SHIFTIN(AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN_MN_BYPASS,
+			      AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN));
+		for (retry = 10000; retry > 0; retry--) {
+			val = PRCM_READ(sc, AM3_PRCM_CM_IDLEST_DPLL_DISP);
+			if ((val & AM3_PRCM_CM_IDLEST_DPLL_DISP_ST_MN_BYPASS) != 0)
+				break;
+			delay(10);
+		}
+
+		/* Set DPLL frequency to 270 MHz */
+		val = __SHIFTIN(270, AM3_PRCM_CM_CLKSEL_DPLL_DISP_DPLL_MULT);
+		val |= __SHIFTIN(24 - 1, AM3_PRCM_CM_CLKSEL_DPLL_DISP_DPLL_DIV);
+		PRCM_WRITE(sc, AM3_PRCM_CM_CLKSEL_DPLL_DISP, val);
+
+		/* Disable MN bypass mode */
+		PRCM_WRITE(sc, AM3_PRCM_CM_CLKMODE_DPLL_DISP,
+		    __SHIFTIN(AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN_LOCK,
+			      AM3_PRCM_CM_CLKMODE_DPLL_DISP_DPLL_EN));
+		for (retry = 10000; retry > 0; retry--) {
+			val = PRCM_READ(sc, AM3_PRCM_CM_IDLEST_DPLL_DISP);
+			if ((val & AM3_PRCM_CM_IDLEST_DPLL_DISP_ST_DPLL_CLK) != 0)
+				break;
+			delay(10);
+		}
+	}
+
+	return am3_prcm_hwmod_enable(sc, tc, enable);
+}
+
 #define	AM3_PRCM_HWMOD_PER(_name, _reg, _parent)	\
 	TI_PRCM_HWMOD((_name), AM3_PRCM_CM_PER + (_reg), (_parent), am3_prcm_hwmod_enable)
+#define	AM3_PRCM_HWMOD_PER_DISP(_name, _reg, _parent)	\
+	TI_PRCM_HWMOD((_name), AM3_PRCM_CM_PER + (_reg), (_parent), am3_prcm_hwmod_enable_display)
 #define	AM3_PRCM_HWMOD_WKUP(_name, _reg, _parent)	\
 	TI_PRCM_HWMOD((_name), AM3_PRCM_CM_WKUP + (_reg), (_parent), am3_prcm_hwmod_enable)
 
@@ -89,6 +141,7 @@ static struct ti_prcm_clk am3_prcm_clks[
 	TI_PRCM_FIXED("FIXED_24MHZ", 24000000),
 	TI_PRCM_FIXED("FIXED_48MHZ", 48000000),
 	TI_PRCM_FIXED("FIXED_96MHZ", 96000000),
+	TI_PRCM_FIXED("DISPLAY_CLK", 270000000),
 	TI_PRCM_FIXED_FACTOR("PERIPH_CLK", 1, 1, "FIXED_48MHZ"),
 	TI_PRCM_FIXED_FACTOR("MMC_CLK", 1, 1, "FIXED_96MHZ"),
 
@@ -127,6 +180,8 @@ static struct ti_prcm_clk am3_prcm_clks[
 	AM3_PRCM_HWMOD_PER("usb_otg_hs", 0x1c, "PERIPH_CLK"),
 
 	AM3_PRCM_HWMOD_PER("rng", 0x90, "PERIPH_CLK"),
+
+	AM3_PRCM_HWMOD_PER_DISP("lcdc", 0x18, "DISPLAY_CLK"),
 };
 
 static int

Index: src/sys/arch/arm/ti/files.ti
diff -u src/sys/arch/arm/ti/files.ti:1.19 src/sys/arch/arm/ti/files.ti:1.20
--- src/sys/arch/arm/ti/files.ti:1.19	Fri Nov  1 11:53:35 2019
+++ src/sys/arch/arm/ti/files.ti	Sun Nov  3 22:59:06 2019
@@ -1,4 +1,4 @@
-#	$NetBSD: files.ti,v 1.19 2019/11/01 11:53:35 jmcneill Exp $
+#	$NetBSD: files.ti,v 1.20 2019/11/03 22:59:06 jmcneill Exp $
 #
 
 file	arch/arm/ti/ti_cpufreq.c	soc_ti
@@ -112,6 +112,15 @@ device	omapfb: rasops16, rasops8, wsemul
 attach	omapfb at fdt with omap3_dss
 file	arch/arm/ti/omap3_dss.c		omap3_dss
 
+define	tilcdcfbbus { }
+device	tilcdc: drmkms, tilcdcfbbus
+attach	tilcdc at fdt with ti_lcdc
+file	arch/arm/ti/ti_lcdc.c		ti_lcdc
+
+device	tifb: tilcdcfbbus, drmfb, wsemuldisplaydev
+attach	tifb at tilcdcfbbus with ti_fb
+file	arch/arm/ti/ti_fb.c		ti_fb
+
 # Memory controller
 device	tigpmc { } : fdt
 attach	tigpmc at fdt with ti_gpmc

Added files:

Index: src/sys/arch/arm/ti/ti_fb.c
diff -u /dev/null src/sys/arch/arm/ti/ti_fb.c:1.1
--- /dev/null	Sun Nov  3 22:59:06 2019
+++ src/sys/arch/arm/ti/ti_fb.c	Sun Nov  3 22:59:06 2019
@@ -0,0 +1,161 @@
+/* $NetBSD: ti_fb.c,v 1.1 2019/11/03 22:59:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015-2019 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_wsdisplay_compat.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ti_fb.c,v 1.1 2019/11/03 22:59:06 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <dev/fdt/fdt_port.h>
+
+#include <drm/drmP.h>
+#include <drm/drmfb.h>
+
+#include <arm/ti/ti_lcdc.h>
+
+static int	ti_fb_match(device_t, cfdata_t, void *);
+static void	ti_fb_attach(device_t, device_t, void *);
+
+static bool	ti_fb_shutdown(device_t, int);
+
+struct ti_fb_softc {
+	struct drmfb_softc	sc_drmfb;
+	device_t		sc_dev;
+	struct tilcdc_framebuffer *sc_fb;
+	struct tilcdcfb_attach_args sc_tfa;
+};
+
+static paddr_t	ti_fb_mmapfb(struct drmfb_softc *, off_t, int);
+static int	ti_fb_ioctl(struct drmfb_softc *, u_long, void *, int,
+			       lwp_t *);
+
+static const struct drmfb_params tifb_drmfb_params = {
+	.dp_mmapfb = ti_fb_mmapfb,
+	.dp_ioctl = ti_fb_ioctl,
+	
+};
+
+CFATTACH_DECL_NEW(ti_fb, sizeof(struct ti_fb_softc),
+	ti_fb_match, ti_fb_attach, NULL, NULL);
+
+static int
+ti_fb_match(device_t parent, cfdata_t cf, void *aux)
+{
+	return 1;
+}
+
+static void
+ti_fb_attach(device_t parent, device_t self, void *aux)
+{
+	struct ti_fb_softc * const sc = device_private(self);
+	struct tilcdcfb_attach_args * const tfa = aux;
+	int error;
+
+	sc->sc_dev = self;
+	sc->sc_tfa = *tfa;
+	sc->sc_fb = to_tilcdc_framebuffer(tfa->tfa_fb_helper->fb);
+
+	aprint_naive("\n");
+	aprint_normal("\n");
+
+#ifdef WSDISPLAY_MULTICONS
+	prop_dictionary_t dict = device_properties(self);
+	const bool is_console = true;
+	prop_dictionary_set_bool(dict, "is_console", is_console);
+#endif
+
+	const struct drmfb_attach_args da = {
+		.da_dev = self,
+		.da_fb_helper = tfa->tfa_fb_helper,
+		.da_fb_sizes = &tfa->tfa_fb_sizes,
+		.da_fb_vaddr = sc->sc_fb->obj->vaddr,
+		.da_fb_linebytes = tfa->tfa_fb_linebytes,
+		.da_params = &tifb_drmfb_params,
+	};
+
+	error = drmfb_attach(&sc->sc_drmfb, &da);
+	if (error) {
+		aprint_error_dev(self, "failed to attach drmfb: %d\n", error);
+		return;
+	}
+
+	pmf_device_register1(self, NULL, NULL, ti_fb_shutdown);
+}
+
+static bool
+ti_fb_shutdown(device_t self, int flags)
+{
+	struct ti_fb_softc * const sc = device_private(self);
+
+	return drmfb_shutdown(&sc->sc_drmfb, flags);
+}
+
+static paddr_t
+ti_fb_mmapfb(struct drmfb_softc *sc, off_t off, int prot)
+{
+	struct ti_fb_softc * const tfb_sc = (struct ti_fb_softc *)sc;
+	struct drm_gem_cma_object *obj = tfb_sc->sc_fb->obj;
+
+	KASSERT(off >= 0);
+	KASSERT(off < obj->dmasize);
+
+	return bus_dmamem_mmap(obj->dmat, obj->dmasegs, 1, off, prot,
+	    BUS_DMA_PREFETCHABLE);
+}
+
+static int
+ti_fb_ioctl(struct drmfb_softc *sc, u_long cmd, void *data, int flag,
+    lwp_t *l)
+{
+	struct wsdisplayio_bus_id *busid;
+	struct wsdisplayio_fbinfo *fbi;
+	struct rasops_info *ri = &sc->sc_genfb.vd.active->scr_ri;
+	int error;
+
+	switch (cmd) {
+	case WSDISPLAYIO_GET_BUSID:
+		busid = data;
+		busid->bus_type = WSDISPLAYIO_BUS_SOC;
+		return 0;
+	case WSDISPLAYIO_GTYPE:
+		*(u_int *)data = WSDISPLAY_TYPE_GENFB;
+		return 0;
+	case WSDISPLAYIO_GET_FBINFO:
+		fbi = data;
+		error = wsdisplayio_get_fbinfo(ri, fbi);
+		fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
+		return error;
+	default:
+		return EPASSTHROUGH;
+	}
+}
Index: src/sys/arch/arm/ti/ti_lcdc.c
diff -u /dev/null src/sys/arch/arm/ti/ti_lcdc.c:1.1
--- /dev/null	Sun Nov  3 22:59:06 2019
+++ src/sys/arch/arm/ti/ti_lcdc.c	Sun Nov  3 22:59:06 2019
@@ -0,0 +1,656 @@
+/* $NetBSD: ti_lcdc.c,v 1.1 2019/11/03 22:59:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ti_lcdc.c,v 1.1 2019/11/03 22:59:06 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+
+#include <uvm/uvm_extern.h>
+#include <uvm/uvm_object.h>
+#include <uvm/uvm_device.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <dev/fdt/fdt_port.h>
+
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/ti_lcdc.h>
+#include <arm/ti/ti_lcdcreg.h>
+
+static const char * const compatible[] = {
+	"ti,am33xx-tilcdc",
+	NULL
+};
+
+enum {
+	TILCDC_PORT_OUTPUT = 0,
+};
+
+static int	tilcdc_match(device_t, cfdata_t, void *);
+static void	tilcdc_attach(device_t, device_t, void *);
+
+static int	tilcdc_set_busid(struct drm_device *, struct drm_master *);
+
+static int	tilcdc_load(struct drm_device *, unsigned long);
+static int	tilcdc_unload(struct drm_device *);
+
+static struct drm_driver tilcdc_driver = {
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.dev_priv_size = 0,
+	.load = tilcdc_load,
+	.unload = tilcdc_unload,
+
+	.gem_free_object = drm_gem_cma_free_object,
+	.mmap_object = drm_gem_or_legacy_mmap_object,
+	.gem_uvm_ops = &drm_gem_cma_uvm_ops,
+
+	.dumb_create = drm_gem_cma_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+
+	.set_busid = tilcdc_set_busid,
+};
+
+CFATTACH_DECL_NEW(ti_lcdc, sizeof(struct tilcdc_softc),
+	tilcdc_match, tilcdc_attach, NULL, NULL);
+
+static int
+tilcdc_mode_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+    int x, int y, int atomic)
+{
+	struct tilcdc_crtc *mixer_crtc = to_tilcdc_crtc(crtc);
+	struct tilcdc_softc * const sc = mixer_crtc->sc;
+	struct tilcdc_framebuffer *sfb = atomic?
+	    to_tilcdc_framebuffer(fb) :
+	    to_tilcdc_framebuffer(crtc->primary->fb);
+
+	const uint32_t paddr = (uint32_t)sfb->obj->dmamap->dm_segs[0].ds_addr;
+	const u_int psize = sfb->obj->dmamap->dm_segs[0].ds_len;
+
+	/* Framebuffer start address */
+	WR4(sc, LCD_LCDDMA_FB0_BASE, paddr);
+	WR4(sc, LCD_LCDDMA_FB0_CEILING, paddr + psize - 1);
+
+	return 0;
+}
+
+static void
+tilcdc_destroy(struct drm_crtc *crtc)
+{
+	drm_crtc_cleanup(crtc);
+}
+
+static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = tilcdc_destroy,
+};
+
+static void
+tilcdc_dpms(struct drm_crtc *crtc, int mode)
+{
+}
+
+static bool
+tilcdc_mode_fixup(struct drm_crtc *crtc,
+    const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+	adjusted_mode->hskew = mode->hsync_end - mode->hsync_start;
+	adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW;
+
+	adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PHSYNC);
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC;
+		
+
+	return true;
+}
+
+static int
+tilcdc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+    struct drm_display_mode *adjusted_mode, int x, int y,
+    struct drm_framebuffer *old_fb)
+{
+	struct tilcdc_crtc *mixer_crtc = to_tilcdc_crtc(crtc);
+	struct tilcdc_softc * const sc = mixer_crtc->sc;
+	uint32_t val;
+	u_int clk_div;
+
+	const u_int hspw = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	const u_int hbp = adjusted_mode->crtc_htotal - adjusted_mode->crtc_hsync_end;
+	const u_int hfp = adjusted_mode->crtc_hsync_start - adjusted_mode->crtc_hdisplay;
+	const u_int vspw = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
+	const u_int vbp = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_end;
+	const u_int vfp = adjusted_mode->crtc_vsync_start - adjusted_mode->crtc_vdisplay;
+
+	const u_int rate = clk_get_rate(sc->sc_clk);
+	for (clk_div = 2; clk_div < 255; clk_div++) {
+		if (rate / clk_div <= (int)adjusted_mode->crtc_clock * 1000)
+			break;
+	}
+	if (clk_div == 255) {
+		device_printf(sc->sc_dev, "couldn't configure pixel clock (%u)\n",
+		    adjusted_mode->crtc_clock);
+		return ERANGE;
+	}
+
+	val = CTRL_RASTER_MODE |
+	      (clk_div << CTRL_DIV_SHIFT);
+	WR4(sc, LCD_CTRL, val);
+
+	val = RASTER_TIMING_0_HFP(hfp) |
+	      RASTER_TIMING_0_HBP(hbp) |
+	      RASTER_TIMING_0_HSW(hspw) |
+	      RASTER_TIMING_0_PPL(adjusted_mode->hdisplay);
+	WR4(sc, LCD_RASTER_TIMING_0, val);
+
+	val = RASTER_TIMING_1_VFP(vfp) |
+	      RASTER_TIMING_1_VBP(vbp) |
+	      RASTER_TIMING_1_VSW(vspw) |
+	      RASTER_TIMING_1_LPP(adjusted_mode->vdisplay);
+	WR4(sc, LCD_RASTER_TIMING_1, val);
+
+	val = RASTER_TIMING_2_HFP(hfp) |
+	      RASTER_TIMING_2_HBP(hbp) |
+	      RASTER_TIMING_2_HSW(hspw) |
+	      RASTER_TIMING_2_LPP(adjusted_mode->vdisplay);
+	/* XXX TDA HDMI TX */
+	val |= RASTER_TIMING_2_IPC;
+	val |= RASTER_TIMING_2_PHSVS;
+	val |= RASTER_TIMING_2_PHSVS_RISE;
+	val |= RASTER_TIMING_2_ACB(255);
+	val |= RASTER_TIMING_2_ACBI(0);
+	WR4(sc, LCD_RASTER_TIMING_2, val);
+
+	val = (4 << LCDDMA_CTRL_BURST_SIZE_SHIFT) |
+	      (0 << LCDDMA_CTRL_TH_FIFO_RDY_SHIFT) |
+	      LCDDMA_CTRL_FB0_ONLY;
+	WR4(sc, LCD_LCDDMA_CTRL, val);
+
+	/* XXX TDA HDMI TX */
+	val = RASTER_CTRL_LCDTFT |
+	      RASTER_CTRL_TFT24 |
+	      RASTER_CTRL_TFT24_UNPACKED |
+	      RASTER_CTRL_REQDLY(0x80) |
+	      RASTER_CTRL_PALMODE_DATA_ONLY;
+	WR4(sc, LCD_RASTER_CTRL, val);
+
+	tilcdc_mode_do_set_base(crtc, old_fb, x, y, 0);
+
+	return 0;
+}
+
+static int
+tilcdc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+    struct drm_framebuffer *old_fb)
+{
+	tilcdc_mode_do_set_base(crtc, old_fb, x, y, 0);
+
+	return 0;
+}
+
+static int
+tilcdc_mode_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+    int x, int y, enum mode_set_atomic state)
+{
+	tilcdc_mode_do_set_base(crtc, fb, x, y, 1);
+
+	return 0;
+}
+
+static void
+tilcdc_disable(struct drm_crtc *crtc)
+{
+}
+
+static void
+tilcdc_prepare(struct drm_crtc *crtc)
+{
+}
+
+static void
+tilcdc_commit(struct drm_crtc *crtc)
+{
+	struct tilcdc_crtc *mixer_crtc = to_tilcdc_crtc(crtc);
+	struct tilcdc_softc * const sc = mixer_crtc->sc;
+	uint32_t val;
+
+	WR4(sc, LCD_CLKC_ENABLE, CLKC_ENABLE_DMA | CLKC_ENABLE_CORE);
+	WR4(sc, LCD_CLKC_RESET, CLKC_RESET_MAIN);
+	delay(100);
+	WR4(sc, LCD_CLKC_RESET, 0);
+
+	val = RD4(sc, LCD_RASTER_CTRL);
+	WR4(sc, LCD_RASTER_CTRL, val | RASTER_CTRL_LCDEN);
+}
+
+static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = {
+	.dpms = tilcdc_dpms,
+	.mode_fixup = tilcdc_mode_fixup,
+	.mode_set = tilcdc_mode_set,
+	.mode_set_base = tilcdc_mode_set_base,
+	.mode_set_base_atomic = tilcdc_mode_set_base_atomic,
+	.disable = tilcdc_disable,
+	.prepare = tilcdc_prepare,
+	.commit = tilcdc_commit,
+};
+
+static void
+tilcdc_encoder_destroy(struct drm_encoder *encoder)
+{
+}
+
+static const struct drm_encoder_funcs tilcdc_encoder_funcs = {
+	.destroy = tilcdc_encoder_destroy,
+};
+
+static void
+tilcdc_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool
+tilcdc_encoder_mode_fixup(struct drm_encoder *encoder,
+    const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void
+tilcdc_encoder_mode_set(struct drm_encoder *encoder,
+    struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void
+tilcdc_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void
+tilcdc_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static const struct drm_encoder_helper_funcs tilcdc_encoder_helper_funcs = {
+	.dpms = tilcdc_encoder_dpms,
+	.mode_fixup = tilcdc_encoder_mode_fixup,
+	.prepare = tilcdc_encoder_prepare,
+	.commit = tilcdc_encoder_commit,
+	.mode_set = tilcdc_encoder_mode_set,
+};
+
+static int
+tilcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
+{
+	struct tilcdc_softc * const sc = device_private(dev);
+	struct drm_device *ddev = sc->sc_ddev;
+
+	if (!activate)
+		return EINVAL;
+
+	sc->sc_crtc.sc = sc;
+
+	WR4(sc, LCD_SYSCONFIG, SYSCONFIG_STANDBY_SMART | SYSCONFIG_IDLE_SMART);
+
+	drm_crtc_init(ddev, &sc->sc_crtc.base, &tilcdc_crtc_funcs);
+	drm_crtc_helper_add(&sc->sc_crtc.base, &tilcdc_crtc_helper_funcs);
+
+	sc->sc_encoder.sc = sc;
+	sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(&sc->sc_crtc.base);
+
+	drm_encoder_init(ddev, &sc->sc_encoder.base, &tilcdc_encoder_funcs,
+	    DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&sc->sc_encoder.base, &tilcdc_encoder_helper_funcs);
+
+	return fdt_endpoint_activate(ep, activate);
+}
+
+static void *
+tilcdc_ep_get_data(device_t dev, struct fdt_endpoint *ep)
+{
+	struct tilcdc_softc * const sc = device_private(dev);
+
+	return &sc->sc_encoder.base;
+}
+
+static int
+tilcdc_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compatible(faa->faa_phandle, compatible);
+}
+
+static void
+tilcdc_attach(device_t parent, device_t self, void *aux)
+{
+	struct tilcdc_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	struct drm_driver * const driver = &tilcdc_driver;
+	prop_dictionary_t dict = device_properties(self);
+	bool is_disabled;
+	bus_addr_t addr;
+	bus_size_t size;
+	int error;
+
+	if (prop_dictionary_get_bool(dict, "disabled", &is_disabled) && is_disabled) {
+		aprint_normal(": TI LCDC (disabled)\n");
+		return;
+	}
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_phandle = faa->faa_phandle;
+	sc->sc_dmat = faa->faa_dmat;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+	sc->sc_clk = ti_prcm_get_hwmod(phandle, 0);
+	if (sc->sc_clk == NULL || clk_enable(sc->sc_clk) != 0) {
+		aprint_error(": couldn't enable module\n");
+		return;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": TI LCDC\n");
+
+	sc->sc_ports.dp_ep_activate = tilcdc_ep_activate;
+	sc->sc_ports.dp_ep_get_data = tilcdc_ep_get_data;
+	fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER);
+
+	sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev);
+	if (sc->sc_ddev == NULL) {
+		aprint_error_dev(self, "couldn't allocate DRM device\n");
+		return;
+	}
+	sc->sc_ddev->dev_private = sc;
+	sc->sc_ddev->bst = sc->sc_bst;
+	sc->sc_ddev->bus_dmat = sc->sc_dmat;
+	sc->sc_ddev->dmat = sc->sc_ddev->bus_dmat;
+	sc->sc_ddev->dmat_subregion_p = false;
+
+	error = -drm_dev_register(sc->sc_ddev, 0);
+	if (error) {
+		drm_dev_unref(sc->sc_ddev);
+		aprint_error_dev(self, "couldn't register DRM device: %d\n",
+		    error);
+		return;
+	}
+
+	aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n",
+	    driver->name, driver->major, driver->minor, driver->patchlevel,
+	    driver->date, sc->sc_ddev->primary->index);
+}
+
+static int
+tilcdc_set_busid(struct drm_device *ddev, struct drm_master *master)
+{
+	struct tilcdc_softc * const sc = tilcdc_private(ddev);
+	char id[32];
+
+	snprintf(id, sizeof(id), "platform:tilcdc:%u", device_unit(sc->sc_dev));
+
+	master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL);
+	if (master->unique == NULL)
+		return -ENOMEM;
+	strcpy(master->unique, id);
+	master->unique_len = strlen(master->unique);
+
+	return 0;
+}
+
+static int
+tilcdc_fb_create_handle(struct drm_framebuffer *fb,
+    struct drm_file *file, unsigned int *handle)
+{
+	struct tilcdc_framebuffer *sfb = to_tilcdc_framebuffer(fb);
+
+	return drm_gem_handle_create(file, &sfb->obj->base, handle);
+}
+
+static void
+tilcdc_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct tilcdc_framebuffer *sfb = to_tilcdc_framebuffer(fb);
+
+	drm_framebuffer_cleanup(fb);
+	drm_gem_object_unreference_unlocked(&sfb->obj->base);
+	kmem_free(sfb, sizeof(*sfb));
+}
+
+static const struct drm_framebuffer_funcs tilcdc_framebuffer_funcs = {
+	.create_handle = tilcdc_fb_create_handle,
+	.destroy = tilcdc_fb_destroy,
+};
+
+static struct drm_framebuffer *
+tilcdc_fb_create(struct drm_device *ddev, struct drm_file *file,
+    struct drm_mode_fb_cmd2 *cmd)
+{
+	struct tilcdc_framebuffer *fb;
+	struct drm_gem_object *gem_obj;
+	int error;
+
+	if (cmd->flags)
+		return NULL;
+
+	gem_obj = drm_gem_object_lookup(ddev, file, cmd->handles[0]);
+	if (gem_obj == NULL)
+		return NULL;
+
+	fb = kmem_zalloc(sizeof(*fb), KM_SLEEP);
+	fb->obj = to_drm_gem_cma_obj(gem_obj);
+	fb->base.pitches[0] = cmd->pitches[0];
+	fb->base.pitches[1] = cmd->pitches[1];
+	fb->base.pitches[2] = cmd->pitches[2];
+	fb->base.offsets[0] = cmd->offsets[0];
+	fb->base.offsets[1] = cmd->offsets[2];
+	fb->base.offsets[2] = cmd->offsets[1];
+	fb->base.width = cmd->width;
+	fb->base.height = cmd->height;
+	fb->base.pixel_format = cmd->pixel_format;
+	fb->base.bits_per_pixel = drm_format_plane_cpp(fb->base.pixel_format, 0) * 8;
+
+	switch (fb->base.pixel_format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_XBGR8888:
+		fb->base.depth = 32;
+		break;
+	default:
+		break;
+	}
+
+	error = drm_framebuffer_init(ddev, &fb->base, &tilcdc_framebuffer_funcs);
+	if (error != 0)
+		goto dealloc;
+
+	return &fb->base;
+
+dealloc:
+	drm_framebuffer_cleanup(&fb->base);
+	kmem_free(fb, sizeof(*fb));
+	drm_gem_object_unreference_unlocked(gem_obj);
+
+	return NULL;
+}
+
+static struct drm_mode_config_funcs tilcdc_mode_config_funcs = {
+	.fb_create = tilcdc_fb_create,
+};
+
+static int
+tilcdc_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes)
+{
+	struct tilcdc_softc * const sc = tilcdc_private(helper->dev);
+	struct drm_device *ddev = helper->dev;
+	struct tilcdc_framebuffer *sfb = to_tilcdc_framebuffer(helper->fb);
+	struct drm_framebuffer *fb = helper->fb;
+	struct tilcdcfb_attach_args tfa;
+	const char *br_wiring;
+	uint32_t pixel_format;
+	int error;
+
+	const u_int width = sizes->surface_width;
+	const u_int height = sizes->surface_height;
+	const u_int pitch = width * (32 / 8);
+
+	br_wiring = fdtbus_get_string(sc->sc_phandle, "blue-and-red-wiring");
+	if (br_wiring && strcmp(br_wiring, "straight") == 0) {
+		pixel_format = DRM_FORMAT_XBGR8888;
+	} else {
+		pixel_format = DRM_FORMAT_XRGB8888;
+	}
+
+	const size_t size = roundup(height * pitch, PAGE_SIZE);
+
+	sfb->obj = drm_gem_cma_create(ddev, size);
+	if (sfb->obj == NULL) {
+		DRM_ERROR("failed to allocate memory for framebuffer\n");
+		return -ENOMEM;
+	}
+
+	fb->pitches[0] = pitch;
+	fb->offsets[0] = 0;
+	fb->width = width;
+	fb->height = height;
+	fb->pixel_format = pixel_format;
+	drm_fb_get_bpp_depth(fb->pixel_format, &fb->depth, &fb->bits_per_pixel);
+
+	error = drm_framebuffer_init(ddev, fb, &tilcdc_framebuffer_funcs);
+	if (error != 0) {
+		DRM_ERROR("failed to initialize framebuffer\n");
+		return error;
+	}
+
+	memset(&tfa, 0, sizeof(tfa));
+	tfa.tfa_drm_dev = ddev;
+	tfa.tfa_fb_helper = helper;
+	tfa.tfa_fb_sizes = *sizes;
+	tfa.tfa_fb_bst = sc->sc_bst;
+	tfa.tfa_fb_dmat = sc->sc_dmat;
+	tfa.tfa_fb_linebytes = helper->fb->pitches[0];
+
+	helper->fbdev = config_found_ia(ddev->dev, "tilcdcfbbus", &tfa, NULL);
+	if (helper->fbdev == NULL) {
+		DRM_ERROR("unable to attach framebuffer\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static struct drm_fb_helper_funcs tilcdc_fb_helper_funcs = {
+	.fb_probe = tilcdc_fb_probe,
+};
+
+static int
+tilcdc_load(struct drm_device *ddev, unsigned long flags)
+{
+	struct tilcdc_softc * const sc = tilcdc_private(ddev);
+	struct tilcdc_fbdev *fbdev;
+	struct fdt_endpoint *ep;
+	int error;
+
+	drm_mode_config_init(ddev);
+	ddev->mode_config.min_width = 0;
+	ddev->mode_config.min_height = 0;
+	ddev->mode_config.max_width = 2048;
+	ddev->mode_config.max_height = 2048;
+	ddev->mode_config.funcs = &tilcdc_mode_config_funcs;
+
+	ep = fdt_endpoint_get_from_index(&sc->sc_ports, TILCDC_PORT_OUTPUT, 0);
+	if (ep == NULL) {
+		aprint_error_dev(sc->sc_dev, "couldn't find endpoint\n");
+		return ENXIO;
+	}
+	error = fdt_endpoint_activate_direct(ep, true);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "couldn't activate endpoint: %d\n", error);
+		return error;
+	}
+
+	fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP);
+
+	drm_fb_helper_prepare(ddev, &fbdev->helper, &tilcdc_fb_helper_funcs);
+
+	error = drm_fb_helper_init(ddev, &fbdev->helper, 1, 1);
+	if (error)
+		goto drmerr;
+
+	fbdev->helper.fb = kmem_zalloc(sizeof(struct tilcdc_framebuffer), KM_SLEEP);
+
+	drm_fb_helper_single_add_all_connectors(&fbdev->helper);
+
+	drm_helper_disable_unused_functions(ddev);
+
+	drm_fb_helper_initial_config(&fbdev->helper, 32);
+
+	return 0;
+
+drmerr:
+	drm_mode_config_cleanup(ddev);
+	kmem_free(fbdev, sizeof(*fbdev));
+
+	return error;
+}
+
+static int
+tilcdc_unload(struct drm_device *ddev)
+{
+	drm_mode_config_cleanup(ddev);
+
+	return 0;
+}
Index: src/sys/arch/arm/ti/ti_lcdc.h
diff -u /dev/null src/sys/arch/arm/ti/ti_lcdc.h:1.1
--- /dev/null	Sun Nov  3 22:59:06 2019
+++ src/sys/arch/arm/ti/ti_lcdc.h	Sun Nov  3 22:59:06 2019
@@ -0,0 +1,110 @@
+/* $NetBSD: ti_lcdc.h,v 1.1 2019/11/03 22:59:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _ARM_TI_TI_LCDC_H
+#define _ARM_TI_TI_LCDC_H
+
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#define DRIVER_AUTHOR		"NetBSD"
+
+#define DRIVER_NAME		"tilcdc"
+#define DRIVER_DESC		"TI LCDC"
+#define DRIVER_DATE		"20191103"
+
+#define DRIVER_MAJOR		1
+#define DRIVER_MINOR		0
+#define DRIVER_PATCHLEVEL	0
+
+struct tilcdc_softc;
+struct tilcdc_framebuffer;
+
+struct tilcdc_vblank {
+	void			*priv;
+	void			(*enable_vblank)(void *);
+	void			(*disable_vblank)(void *);
+	uint32_t		(*get_vblank_counter)(void *);
+};
+
+struct tilcdc_crtc {
+	struct drm_crtc		base;
+	struct tilcdc_softc	*sc;
+};
+
+struct tilcdc_encoder {
+	struct drm_encoder	base;
+	struct tilcdc_softc	*sc;
+};
+
+struct tilcdc_softc {
+	device_t		sc_dev;
+	struct drm_device	*sc_ddev;
+
+	bus_space_tag_t		sc_bst;
+	bus_space_handle_t	sc_bsh;
+	bus_dma_tag_t		sc_dmat;
+
+	struct clk		*sc_clk;
+	int			sc_phandle;
+
+	struct tilcdc_crtc	sc_crtc;
+	struct tilcdc_encoder	sc_encoder;
+	struct tilcdc_vblank	sc_vbl;
+
+	struct fdt_device_ports	sc_ports;
+};
+
+struct tilcdc_framebuffer {
+	struct drm_framebuffer	base;
+	struct drm_gem_cma_object *obj;
+};
+
+struct tilcdc_fbdev {
+	struct drm_fb_helper	helper;
+};
+
+struct tilcdcfb_attach_args {
+	struct drm_device	*tfa_drm_dev;
+	struct drm_fb_helper	*tfa_fb_helper;
+	struct drm_fb_helper_surface_size tfa_fb_sizes;
+	bus_space_tag_t		tfa_fb_bst;
+	bus_dma_tag_t		tfa_fb_dmat;
+	uint32_t		tfa_fb_linebytes;
+};
+
+#define tilcdc_private(ddev)		(ddev)->dev_private
+#define	to_tilcdc_framebuffer(x)	container_of(x, struct tilcdc_framebuffer, base)
+#define	to_tilcdc_crtc(x)		container_of(x, struct tilcdc_crtc, base)
+
+#define	RD4(sc, reg)			\
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define	WR4(sc, reg, val)		\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+#endif /* _ARM_TI_TI_LCDC_H */
Index: src/sys/arch/arm/ti/ti_lcdcreg.h
diff -u /dev/null src/sys/arch/arm/ti/ti_lcdcreg.h:1.1
--- /dev/null	Sun Nov  3 22:59:06 2019
+++ src/sys/arch/arm/ti/ti_lcdcreg.h	Sun Nov  3 22:59:06 2019
@@ -0,0 +1,136 @@
+/* $NetBSD: ti_lcdcreg.h,v 1.1 2019/11/03 22:59:06 jmcneill Exp $ */
+/*-
+ * Copyright 2013 Oleksandr Tymoshenko <go...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define	LCD_PID			0x00
+#define	LCD_CTRL		0x04
+#define		CTRL_DIV_MASK		0xff
+#define		CTRL_DIV_SHIFT		8
+#define		CTRL_AUTO_UFLOW_RESTART	(1 << 1)
+#define		CTRL_RASTER_MODE	1
+#define		CTRL_LIDD_MODE		0
+#define	LCD_LIDD_CTRL		0x0C
+#define	LCD_LIDD_CS0_CONF	0x10
+#define	LCD_LIDD_CS0_ADDR	0x14
+#define	LCD_LIDD_CS0_DATA	0x18
+#define	LCD_LIDD_CS1_CONF	0x1C
+#define	LCD_LIDD_CS1_ADDR	0x20
+#define	LCD_LIDD_CS1_DATA	0x24
+#define	LCD_RASTER_CTRL		0x28
+#define		RASTER_CTRL_TFT24_UNPACKED	(1 << 26)
+#define		RASTER_CTRL_TFT24		(1 << 25)
+#define		RASTER_CTRL_STN565		(1 << 24)
+#define		RASTER_CTRL_TFTMAP		(1 << 23)
+#define		RASTER_CTRL_NIBMODE		(1 << 22)
+#define		RASTER_CTRL_PALMODE_PALETTE_AND_DATA (0 << 20)
+#define		RASTER_CTRL_PALMODE_PALETTE_ONLY (1 << 20)
+#define		RASTER_CTRL_PALMODE_DATA_ONLY	(2 << 20)
+#define		RASTER_CTRL_REQDLY(v)		((v) << 12)
+#define		RASTER_CTRL_MONO8B		(1 << 9)
+#define		RASTER_CTRL_RDORDER		(1 << 8)
+#define		RASTER_CTRL_LCDTFT		(1 << 7)
+#define		RASTER_CTRL_LCDBW		(1 << 1)
+#define		RASTER_CTRL_LCDEN		(1 << 0)
+#define	LCD_RASTER_TIMING_0	0x2C
+#define		RASTER_TIMING_0_HBP(v)		((((v) - 1) & 0xff) << 24)
+#define		RASTER_TIMING_0_HFP(v)		((((v) - 1) & 0xff) << 16)
+#define		RASTER_TIMING_0_HSW(v)		((((v) - 1) & 0x3f) << 10)
+#define		RASTER_TIMING_0_PPL(w) \
+		(((((w) - 1) >> 7) & 0x8) | ((((w) - 1) >> 0) & 0x3f0))
+#define	LCD_RASTER_TIMING_1	0x30
+#define		RASTER_TIMING_1_VBP(v)		(((v) & 0xff) << 24)
+#define		RASTER_TIMING_1_VFP(v)		(((v) & 0xff) << 16)
+#define		RASTER_TIMING_1_VSW(v)		((((v) - 1) & 0x3f) << 10)
+#define		RASTER_TIMING_1_LPP(h)		((((h) - 1) & 0x3ff) << 0)
+#define	LCD_RASTER_TIMING_2	0x34
+#define		RASTER_TIMING_2_HSW(v)		(((((v) - 1) >> 6) & 0xf) << 27)
+#define		RASTER_TIMING_2_LPP(h)		(((h) & 0x400) ? (1 << 26) : 0)
+#define		RASTER_TIMING_2_PHSVS		(1 << 25)
+#define		RASTER_TIMING_2_PHSVS_RISE	(1 << 24)
+#define		RASTER_TIMING_2_PHSVS_FALL	(0 << 24)
+#define		RASTER_TIMING_2_IOE		(1 << 23)
+#define		RASTER_TIMING_2_IPC		(1 << 22)
+#define		RASTER_TIMING_2_IHS		(1 << 21)
+#define		RASTER_TIMING_2_IVS		(1 << 20)
+#define		RASTER_TIMING_2_ACBI(x)		((x) << 16)
+#define		RASTER_TIMING_2_ACB(x)		((x) << 8)
+#define		RASTER_TIMING_2_HBP(v)		((((v) - 1) >> 4) & 0x30)
+#define		RASTER_TIMING_2_HFP(v)		((((v) - 1) >> 8) & 0x3)
+#define	LCD_RASTER_SUBPANEL	0x38
+#define	LCD_RASTER_SUBPANEL2	0x3C
+#define	LCD_LCDDMA_CTRL		0x40
+#define		LCDDMA_CTRL_DMA_MASTER_PRIO_SHIFT		16
+#define		LCDDMA_CTRL_TH_FIFO_RDY_SHIFT	8
+#define		LCDDMA_CTRL_BURST_SIZE_SHIFT	4
+#define		LCDDMA_CTRL_BYTES_SWAP		(1 << 3)
+#define		LCDDMA_CTRL_BE			(1 << 1)
+#define		LCDDMA_CTRL_FB0_FB1		(1 << 0)
+#define		LCDDMA_CTRL_FB0_ONLY		(0 << 0)
+#define	LCD_LCDDMA_FB0_BASE	0x44
+#define	LCD_LCDDMA_FB0_CEILING	0x48
+#define	LCD_LCDDMA_FB1_BASE	0x4C
+#define	LCD_LCDDMA_FB1_CEILING	0x50
+#define	LCD_SYSCONFIG		0x54
+#define		SYSCONFIG_STANDBY_FORCE		(0 << 4)
+#define		SYSCONFIG_STANDBY_NONE		(1 << 4)
+#define		SYSCONFIG_STANDBY_SMART		(2 << 4)
+#define		SYSCONFIG_IDLE_FORCE		(0 << 2)
+#define		SYSCONFIG_IDLE_NONE		(1 << 2)
+#define		SYSCONFIG_IDLE_SMART		(2 << 2)
+#define	LCD_IRQSTATUS_RAW	0x58
+#define	LCD_IRQSTATUS		0x5C
+#define	LCD_IRQENABLE_SET	0x60
+#define	LCD_IRQENABLE_CLEAR	0x64
+#define		IRQ_EOF1		(1 << 9)
+#define		IRQ_EOF0		(1 << 8)
+#define		IRQ_PL			(1 << 6)
+#define		IRQ_FUF			(1 << 5)
+#define		IRQ_ACB			(1 << 3)
+#define		IRQ_SYNC_LOST		(1 << 2)
+#define		IRQ_RASTER_DONE		(1 << 1)
+#define		IRQ_FRAME_DONE		(1 << 0)
+#define	LCD_CLKC_ENABLE		0x6C
+#define		CLKC_ENABLE_DMA		(1 << 2)
+#define		CLKC_ENABLE_LIDD	(1 << 1)
+#define		CLKC_ENABLE_CORE	(1 << 0)
+#define	LCD_CLKC_RESET		0x70
+#define		CLKC_RESET_MAIN		(1 << 3)
+#define		CLKC_RESET_DMA		(1 << 2)
+#define		CLKC_RESET_LIDD		(1 << 1)
+#define		CLKC_RESET_CORE		(1 << 0)
+
+/* 16-Entry Palette/Buffer Format */
+#define PALETTE_BPP_1			(0 << 12)
+#define PALETTE_BPP_2			(1 << 12)
+#define PALETTE_BPP_4			(2 << 12)
+#define PALETTE_BPP_8			(3 << 12)
+#define PALETTE_BPP_XX			(4 << 12)
+#define PALETTE_MONO(v)			((v) & 0xf)
+#define PALETTE_RED(r)			(((r) & 0xf) << 8)
+#define PALETTE_GREEN(g)		(((g) & 0xf) << 4)
+#define PALETTE_BLUE(b)			(((b) & 0xf) << 0)
+#define PALETTE_COLOR(r, g, b) \
+			(PALETTE_RED(r) | PALETTE_GREEN(g) | PALETTE_BLUE(b))

Reply via email to