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