This patch introduces preliminary support for the display hardware
onboard the am335x/BeagleBone Black. Included are two drivers,
amdisplay(4) and nxptda(4), which run the LCD controller and HDMI PHY
transmitter respectively. This patch follows work I've mailed to this
list found here:

https://marc.info/?t=147047204600001&r=1&w=2

The code below is a complete (fourth!) rewrite. It completely and
correctly controls the hardware but still needs to be plugged into the
rest of the system (wsdisplay for syscons, wsfb for Xorg, etc.) before
it is useful. In the meantime, I feel that it is review-ready and worth
the time for a dev to look over.

Here is a demonstration of my code working where the framebuffer is
filled with entropy from arc4random_buf() every frame:

https://www.youtube.com/watch?v=yCzFyZlfbzQ

Here is the man page in HTML format for convenience:

https://ce.gl/amdisplay.4.html

Here is a link to the patch below for when the list mangles it:

https://ce.gl/amdisplay-apr2.diff.txt

Note: this driver currently only supports 640x480x16 mode, but is only
one clock/math problem away from higher modes. Yes, I am aware of fdt
simple-framebuffer node and yes, this code accomplishes useful things
which the fdt framebuffer cannot.

Ian

Index: sys/arch/armv7/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/armv7/conf/GENERIC,v
retrieving revision 1.71
diff -u -p -p -u -r1.71 GENERIC
--- sys/arch/armv7/conf/GENERIC 23 Jan 2017 22:43:17 -0000      1.71
+++ sys/arch/armv7/conf/GENERIC 3 Apr 2017 05:24:32 -0000
@@ -81,6 +81,9 @@ sdmmc*                at ommmc?               # SD/MMC bus
 
 omehci*                at fdt?                 # EHCI
 usb*           at omehci?
+nxptda*                at iic?                 # TDA19988 HDMI PHY
+amdisplay*     at fdt?                 # AM335x LCD controller
+wsdisplay*     at amdisplay?
 
 # Sunxi A1x/A20 SoC
 sxiintc*       at fdt?                 # A1x interrupt controller
Index: sys/arch/armv7/omap/am335x_prcmreg.h
===================================================================
RCS file: /cvs/src/sys/arch/armv7/omap/am335x_prcmreg.h,v
retrieving revision 1.4
diff -u -p -p -u -r1.4 am335x_prcmreg.h
--- sys/arch/armv7/omap/am335x_prcmreg.h        18 Mar 2014 07:34:17 -0000      
1.4
+++ sys/arch/armv7/omap/am335x_prcmreg.h        3 Apr 2017 05:24:33 -0000
@@ -20,6 +20,7 @@
 #define AM335X_CLKCTRL_MODULEMODE_MASK         0x00000003
 
 #define PRCM_AM335X_CM_PER             0x0000
+#define PRCM_AM335X_LCDC_CLKCTRL       0x0018
 #define PRCM_AM335X_USB0_CLKCTRL       0x001c
 #define PRCM_AM335X_TPTC0_CLKCTRL      0x0024
 #define PRCM_AM335X_MMC0_CLKCTRL       0x003c
@@ -38,6 +39,10 @@
 #define PRCM_AM335X_CM_WKUP            0x0400
 #define PRCM_AM335X_GPIO0_CLKCTRL      0x0408
 #define PRCM_AM335X_TIMER0_CLKCTRL     0x0410
+#define PRCM_AM335X_DISP_IDLEST                0x0448
+#define PRCM_AM335X_DISP_CLKSEL                0x0454
+#define PRCM_AM335X_DISP_CLKMODE       0x0498
+#define PRCM_AM335X_DISP_M2            0x04a4
 #define PRCM_AM335X_I2C0_CLKCTRL       0x04b8
 #define PRCM_AM335X_CM_DPLL            0x0500
 #define PRCM_AM335X_CLKSEL_TIMER2_CLK  0x0508
Index: sys/arch/armv7/omap/amdisplay.c
===================================================================
RCS file: sys/arch/armv7/omap/amdisplay.c
diff -N sys/arch/armv7/omap/amdisplay.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/armv7/omap/amdisplay.c     3 Apr 2017 05:24:33 -0000
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <i...@kremlin.cc>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+#include <dev/cons.h>
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsdisplayvar.h>
+#include <dev/wscons/wscons_callbacks.h>
+#include <dev/rasops/rasops.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pinctrl.h>
+#include <dev/ofw/fdt.h>
+#include <machine/fdt.h>
+
+#include <armv7/omap/prcmvar.h>
+#include <armv7/omap/amdisplayreg.h>
+#include <armv7/omap/nxptdavar.h>
+
+#ifdef LCD_DEBUG
+int lcd_dbg_thresh = 20;
+#define DPRINTF(n,s)   do { if ((n) <= lcd_dbg_thresh) printf s; } while (0)
+#else
+#define DPRINTF(n,s)   do {} while (0)
+#endif
+
+#define str(X) #X
+#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
+#define PALETTE_BPP 0x4000
+
+#define HREAD4(sc, reg)                                                        
\
+       (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
+#define HWRITE4(sc, reg, val)                                          \
+       bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+#define HSET4(sc, reg, bits)                                           \
+       HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
+#define HCLR4(sc, reg, bits)                                           \
+       HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
+
+struct amdisplay_softc {
+       struct device           sc_dev;
+       bus_space_tag_t         sc_iot;
+       bus_space_handle_t      sc_ioh;
+       bus_dma_tag_t           sc_dmat;
+       void                    *sc_ih;
+
+       int                     sc_flags;
+#define LCD_RESET_PENDING (1 << 0)
+#define LCD_MODE_COMPAT          (1 << 1)
+#define LCD_MODE_ALLOC   (1 << 2)
+
+       struct edid_info        *sc_edid;
+       struct videomode        *sc_active_mode;
+       int                     sc_active_depth;
+
+       bus_dmamap_t            sc_fb0_dma;
+       bus_dma_segment_t       sc_fb0_dma_segs[1];
+       void                    *sc_fb0;
+       int                     sc_fb_dma_nsegs;
+       bus_size_t              sc_fb_size;
+
+       struct rasops_info      *sc_ro;
+};
+
+struct wsscreen_descr amdisplay_stdscreen = {
+       "std", 100, 100,
+       NULL,
+       0, 0
+};
+
+const struct wsscreen_descr *amdisplay_scrlist[] = {
+       &amdisplay_stdscreen,
+};
+
+struct wsscreen_list amdisplay_screenlist = {
+       nitems(amdisplay_scrlist), amdisplay_scrlist
+};
+
+int  amdisplay_match(struct device *, void *, void *);
+void amdisplay_attach(struct device *, struct device *, void *);
+int  amdisplay_detach(struct device *, int);
+int  amdisplay_activate(struct device *, int);
+int  amdisplay_intr(void *);
+
+int  amdisplay_ioctl(void *, u_long, caddr_t, int, struct proc *);
+int  amdisplay_alloc_screen(void *, const struct wsscreen_descr *,
+       void **, int *, int *, long *);
+void amdisplay_free_screen(void *, void *);
+int  amdisplay_show_screen(void *, void *, int,
+       void (*)(void *, int, int), void *);
+void amdisplay_doswitch(void *, void *);
+int  amdisplay_load_font(void *, void *, struct wsdisplay_font *);
+int  amdisplay_list_font(void *, struct wsdisplay_font *);
+int  amdisplay_getchar(void *, int, int, struct wsdisplay_charcell *);
+void amdisplay_burner(void *, u_int, u_int);
+paddr_t amdisplay_mmap(void *, off_t, int);
+
+int  amdisplay_setup_dma(struct amdisplay_softc *);
+void amdisplay_conf_crt_timings(struct amdisplay_softc *);
+
+struct wsdisplay_accessops amdisplay_accessops = {
+       .ioctl = amdisplay_ioctl,
+       .mmap = amdisplay_mmap,
+       .alloc_screen = amdisplay_alloc_screen,
+       .free_screen = amdisplay_free_screen,
+       .show_screen = amdisplay_show_screen,
+       .getchar = amdisplay_getchar,
+       .load_font = amdisplay_load_font,
+       .list_font = amdisplay_list_font,
+       .burn_screen = amdisplay_burner
+};
+
+struct cfattach amdisplay_ca = {
+       sizeof(struct amdisplay_softc), amdisplay_match, amdisplay_attach, 
amdisplay_detach
+};
+
+struct cfdriver amdisplay_cd = {
+       NULL, "amdisplay", DV_DULL
+};
+
+void
+preg(uint32_t reg, char *rn, struct amdisplay_softc *sc)
+{
+       uint32_t read;
+
+       read = HREAD4(sc, reg);
+       DPRINTF(16, ("%s: %s: 0x%08x\n", DEVNAME(sc), rn, read));
+}
+
+void
+dumpregs(struct amdisplay_softc *sc)
+{
+       preg(LCD_PID, str(AMDISPLAY_PID), sc);
+       preg(LCD_CTRL, str(AMDISPLAY_CTRL), sc);
+       preg(LCD_RASTER_CTRL, str(AMDISPLAY_RASTER_CTRL), sc);
+       preg(LCD_RASTER_TIMING_0, str(AMDISPLAY_RASTER_TIMING_0), sc);
+       preg(LCD_RASTER_TIMING_1, str(AMDISPLAY_RASTER_TIMING_1), sc);
+       preg(LCD_RASTER_TIMING_2, str(AMDISPLAY_RASTER_TIMING_2), sc);
+       preg(LCD_RASTER_SUBPANEL, str(AMDISPLAY_RASTER_SUBPANEL), sc);
+       preg(LCD_RASTER_SUBPANEL_2, str(AMDISPLAY_RASTER_SUBPANEL_2), sc);
+       preg(LCD_LCDDMA_CTRL, str(AMDISPLAY_LCDDMA_CTRL), sc);
+
+       /* accessing these regs is liable to occur during CPU lock out period */
+       #if 0
+       preg(LCD_LCDDMA_FB0_BASE, str(AMDISPLAY_LCDDMA_FB0_BASE), sc);
+       preg(LCD_LCDDMA_FB0_CEILING, str(AMDISPLAY_LCDDMA_FB0_CEILING), sc);
+       preg(LCD_LCDDMA_FB1_BASE, str(AMDISPLAY_LCDDMA_FB1_BASE), sc);
+       preg(LCD_LCDDMA_FB1_CEILING, str(AMDISPLAY_LCDDMA_FB1_CEILING), sc);
+       #endif
+
+       preg(LCD_SYSCONFIG, str(AMDISPLAY_SYSCONFIG), sc);
+       preg(LCD_IRQSTATUS_RAW, str(AMDISPLAY_IRQSTATUS_RAW), sc);
+       preg(LCD_IRQSTATUS, str(AMDISPLAY_IRQSTATUS), sc);
+       preg(LCD_IRQENABLE_SET, str(AMDISPLAY_IRQENABLE_SET), sc);
+       preg(LCD_IRQENABLE_CLEAR, str(AMDISPLAY_IRQENABLE_CLEAR), sc);
+       preg(LCD_CLKC_ENABLE, str(AMDISPLAY_CLKC_ENABLE), sc);
+       preg(LCD_CLKC_RESET, str(AMDISPLAY_CLKC_RESET), sc);
+}
+
+int
+amdisplay_match(struct device *parent, void *v, void *aux)
+{
+       struct fdt_attach_args *faa = aux;
+       return OF_is_compatible(faa->fa_node, "ti,am33xx-tilcdc");
+}
+
+void
+amdisplay_attach(struct device *parent, struct device *self, void *args)
+{
+       struct amdisplay_softc  *sc = (struct amdisplay_softc *) self;
+       struct fdt_attach_args  *faa = args;
+       struct wsemuldisplaydev_attach_args wsaa;
+       struct edid_info edid;
+
+       uint32_t irq, reg;
+       uint8_t *edid_buf;
+       int stride, i = 0;
+
+       sc->sc_iot  = faa->fa_iot;
+       sc->sc_dmat = faa->fa_dmat;
+       if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+           faa->fa_reg[0].size, 0, &sc->sc_ioh))
+               panic("%s: bus_space_map failed!", __func__);
+
+       /* enable clock module */
+       prcm_enablemodule(PRCM_LCDC);
+
+       /* force ourselves out of standby/idle states */
+       reg = HREAD4(sc, LCD_SYSCONFIG);
+       reg &= ~(LCD_SYSCONFIG_STANDBYMODE | LCD_SYSCONFIG_IDLEMODE);
+       reg |= (0x2 << LCD_SYSCONFIG_STANDBYMODE_SHAMT)
+           |  (0x2 << LCD_SYSCONFIG_IDLEMODE_SHAMT);
+       HWRITE4(sc, LCD_SYSCONFIG, reg);
+
+       irq = faa->fa_intr[0];
+       sc->sc_ih = arm_intr_establish(irq, IPL_BIO, amdisplay_intr, sc, 
DEVNAME(sc));
+
+       printf("\n");
+
+       /* read/parse EDID bits from TDA19988 HDMI PHY */
+       edid_buf = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO);
+       sc->sc_active_mode = malloc(sizeof(struct videomode), M_DEVBUF,
+           M_WAITOK | M_ZERO);
+       sc->sc_flags |= LCD_MODE_ALLOC;
+
+       if (nxptda_get_edid(edid_buf, EDID_LENGTH)
+           || edid_parse(edid_buf, &edid)) {
+               printf("%s: no display attached.\n", DEVNAME(sc));
+               free(edid_buf, M_DEVBUF, EDID_LENGTH);
+               amdisplay_detach(self, 0);
+               return;
+       }
+
+       sc->sc_edid = &edid;
+
+       /* check to make sure default VGA mode is supported */
+       for (; i < edid.edid_nmodes - 1; i++)
+           if (!strcmp(edid.edid_modes[i].name, "640x480x60"))
+               sc->sc_flags |= LCD_MODE_COMPAT;
+       i = 0;
+
+       if (!ISSET(sc->sc_flags, LCD_MODE_COMPAT)) {
+           printf("%s: no suitable video modes found\n", DEVNAME(sc));
+           free(edid_buf, M_DEVBUF, EDID_LENGTH);
+           amdisplay_detach(self, 0);
+           return;
+       }
+
+       /* set VGA videomode */
+       for (; i < videomode_count; i++)
+           if (!strcmp("640x480x60", videomode_list[i].name))
+               memcpy(sc->sc_active_mode, &videomode_list[i], sizeof(struct 
videomode));
+
+       sc->sc_active_mode->flags |= VID_HSKEW;
+       sc->sc_active_depth = 16;
+
+       /* configure DMA framebuffer */
+       if (amdisplay_setup_dma(sc)) {
+           printf("%s: couldn't allocate DMA framebuffer\n", DEVNAME(sc));
+           free(edid_buf, M_DEVBUF, EDID_LENGTH);
+           amdisplay_detach(self, 0);
+           return;
+       }
+
+       /* setup rasops */
+       stride = sc->sc_active_mode->hdisplay * sc->sc_active_depth;
+
+       sc->sc_ro = malloc(sizeof(struct rasops_info), M_DEVBUF, M_ZERO | 
M_WAITOK);
+       sc->sc_ro->ri_depth  = sc->sc_active_depth;
+       sc->sc_ro->ri_width  = sc->sc_active_mode->hdisplay;
+       sc->sc_ro->ri_height = sc->sc_active_mode->vdisplay;
+       sc->sc_ro->ri_stride = stride;
+       sc->sc_ro->ri_bits   = sc->sc_fb0;
+       sc->sc_ro->ri_rnum   = 5;
+       sc->sc_ro->ri_gnum   = 6;
+       sc->sc_ro->ri_bnum   = 5;
+       sc->sc_ro->ri_rpos   = 0;
+       sc->sc_ro->ri_gpos   = 5;
+       sc->sc_ro->ri_bpos   = 11;
+
+       if (rasops_init(sc->sc_ro, 100, 100)) {
+           printf("%s: no rasops\n", DEVNAME(sc));
+           amdisplay_detach(self, 0);
+           return;
+       }
+
+       /* ensure controller is off */
+       HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN);
+       delay(100);
+
+       /* set clock divisor needed for 640x480 VGA timings */
+       reg = HREAD4(sc, LCD_CTRL);
+       reg &= ~LCD_CTRL_CLKDIV;
+       reg |= (0x2 << LCD_CTRL_CLKDIV_SHAMT);
+
+       /* select raster mode & reset-on-underflow, write */
+       reg |= LCD_CTRL_MODESEL;
+       HWRITE4(sc, LCD_CTRL, reg);
+
+       /* set stn565 + active matrix + pallete loading only mode, delay */
+       reg = HREAD4(sc, LCD_RASTER_CTRL);
+       reg &= 0xF8000C7C;
+       reg |= (LCD_RASTER_CTRL_LCDTFT)
+           |  (0x02  << LCD_RASTER_CTRL_PALMODE_SHAMT)
+           |  (0xFF << LCD_RASTER_CTRL_REQDLY_SHAMT);
+       HWRITE4(sc, LCD_RASTER_CTRL, reg);
+
+       /* set timing values */
+       amdisplay_conf_crt_timings(sc);
+
+       /* configure HDMI transmitter (TDA19988) with our mode details */
+       nxptda_set_videomode(sc->sc_active_mode);
+
+       /* latch pins/pads according to fdt node */
+       pinctrl_byphandle(LCD_FDT_PHANDLE);
+
+       /* write bpp empty palette into framebuffer */
+       memset((void *)sc->sc_fb0, 0, 0x20);
+       *(unsigned int *)sc->sc_fb0 = PALETTE_BPP;
+       sc->sc_fb0 += 0x20;
+
+       /* configure DMA transfer settings */
+       reg = HREAD4(sc, LCD_LCDDMA_CTRL);
+       reg &= ~(LCD_LCDDMA_CTRL_DMA_MASTER_PRIO
+           | LCD_LCDDMA_CTRL_TH_FIFO_READY
+           | LCD_LCDDMA_CTRL_BURST_SIZE
+           | LCD_LCDDMA_CTRL_BYTE_SWAP
+           | LCD_LCDDMA_CTRL_BIGENDIAN
+           | LCD_LCDDMA_CTRL_FRAME_MODE);
+       reg |= (0x4 << LCD_LCDDMA_CTRL_BURST_SIZE_SHAMT);
+       HWRITE4(sc, LCD_LCDDMA_CTRL, reg); 
+
+       /* set framebuffer location + bounds */
+       HWRITE4(sc, LCD_LCDDMA_FB0, sc->sc_fb0_dma_segs[0].ds_addr);
+       HWRITE4(sc, LCD_LCDDMA_FB0_CEIL, (sc->sc_fb0_dma_segs[0].ds_addr
+           + sc->sc_fb_size) - 0);
+
+       /* enable all intrs. */
+       reg = 0;
+       reg |= (LCD_IRQ_EOF1 | LCD_IRQ_EOF0 | LCD_IRQ_PL | LCD_IRQ_FUF |
+               LCD_IRQ_ACB | LCD_IRQ_SYNC | LCD_IRQ_RR_DONE | LCD_IRQ_DONE);
+
+       HWRITE4(sc, LCD_IRQENABLE_SET, reg);
+
+       /* enable dma & core clocks */
+       HSET4(sc, LCD_CLKC_ENABLE, LCD_CLKC_ENABLE_DMA_CLK_EN
+           | LCD_CLKC_ENABLE_CORE_CLK_EN
+           | LCD_CLKC_ENABLE_LIDD_CLK_EN);
+
+       /* perform controller clock reset */
+       HSET4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST);
+       delay(100);
+       HCLR4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST);
+
+       /* attach console */
+       wsaa.console = 0;
+       wsaa.scrdata = &amdisplay_screenlist;
+       wsaa.accessops = &amdisplay_accessops;
+       wsaa.accesscookie = sc;
+       wsaa.defaultscreens = 0;
+
+//     wsdisplay_cnattach(&amdisplay_stdscreen, sc->sc_ro, sc->sc_ro->ri_ccol,
+//         sc->sc_ro->ri_crow, 0);
+//     config_found(self, &wsaa, wsemuldisplaydevprint);
+
+       /* enable controller */
+       HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN);
+
+       dumpregs(sc);
+}
+
+int
+amdisplay_detach(struct device *self, int flags)
+{
+       struct amdisplay_softc *sc = (struct amdisplay_softc *)self;
+
+       if (sc->sc_ro)
+           free(sc->sc_ro, M_DEVBUF, sizeof(struct rasops_info));
+
+       if (sc->sc_edid)
+           free(sc->sc_edid, M_DEVBUF, sizeof(struct edid_info));
+
+       if (ISSET(sc->sc_flags, LCD_MODE_ALLOC))
+           free(sc->sc_active_mode, M_DEVBUF, sizeof(struct videomode));
+
+       if (!sc->sc_fb0)
+           return 0;
+
+       bus_dmamap_sync(sc->sc_dmat, sc->sc_fb0_dma, 0, sc->sc_fb_size,
+           BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+       bus_dmamap_unload(sc->sc_dmat, sc->sc_fb0_dma);
+       bus_dmamem_unmap(sc->sc_dmat, (caddr_t)(sc->sc_fb0), sc->sc_fb_size);
+       bus_dmamem_free(sc->sc_dmat, sc->sc_fb0_dma_segs, sc->sc_fb_dma_nsegs);
+       bus_dmamap_destroy(sc->sc_dmat, sc->sc_fb0_dma);
+
+       return 0;
+}
+
+int
+amdisplay_intr(void *arg)
+{
+       struct amdisplay_softc *sc = arg;
+       uint32_t reg, write_through;
+
+       reg = HREAD4(sc, LCD_IRQSTATUS);
+       HWRITE4(sc, LCD_IRQSTATUS, reg);
+
+       /* make sure we didn't just cache the ack */
+       write_through = HREAD4(sc, LCD_IRQSTATUS);
+
+       DPRINTF(25, ("%s: intr 0x%08x\n", DEVNAME(sc), reg));
+
+       if (ISSET(reg, LCD_IRQ_PL)) {
+           DPRINTF(10, ("%s: palette loaded, irq: 0x%08x\n",
+               DEVNAME(sc), reg));
+           HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN);
+           delay(100);
+           memset(sc->sc_fb0, 0, sc->sc_fb_size);
+           HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_PALMODE);
+           HSET4(sc, LCD_RASTER_CTRL, 0x2 << LCD_RASTER_CTRL_PALMODE_SHAMT);
+           HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN);
+       } if (ISSET(reg, LCD_IRQ_FUF)) { 
+           DPRINTF(15, ("%s: FIFO underflow\n", DEVNAME(sc)));
+       } if (ISSET(reg, LCD_IRQ_SYNC)) {
+           sc->sc_flags |= LCD_RESET_PENDING;
+           DPRINTF(18, ("%s: sync lost\n", DEVNAME(sc)));
+       } if (ISSET(reg, LCD_IRQ_RR_DONE)) { /* use ping-pong here XXX */
+           DPRINTF(21, ("%s: frame done\n", DEVNAME(sc)));
+           HWRITE4(sc, LCD_LCDDMA_FB0, sc->sc_fb0_dma_segs[0].ds_addr);
+           HWRITE4(sc, LCD_LCDDMA_FB0_CEIL, (sc->sc_fb0_dma_segs[0].ds_addr
+               + sc->sc_fb_size) - 1);
+       } if (ISSET(reg, LCD_IRQ_EOF0)) {
+           DPRINTF(21, ("%s: framebuffer 0 done\n", DEVNAME(sc)));
+       } if (ISSET(reg, LCD_IRQ_EOF1)) {
+           DPRINTF(21, ("%s: framebuffer 1 done\n", DEVNAME(sc)));
+       } if (ISSET(reg, LCD_IRQ_DONE)) {
+           if (ISSET(sc->sc_flags, LCD_RESET_PENDING)) {
+               HWRITE4(sc, LCD_IRQSTATUS, 0xFFFFFFFF); /* XXX */
+               HSET4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST);
+               delay(10);
+               HCLR4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST);
+               HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN);
+               sc->sc_flags &= ~LCD_RESET_PENDING;
+           }
+           DPRINTF(15, ("%s: last frame done\n", DEVNAME(sc)));
+       } if (ISSET(reg, LCD_IRQ_ACB)) {
+           DPRINTF(15, ("%s: AC bias event\n", DEVNAME(sc)));
+       }
+
+       HWRITE4(sc, LCD_IRQ_END, 0);
+       reg = HREAD4(sc, LCD_IRQ_END);
+
+       return 0;
+}
+
+int
+amdisplay_setup_dma(struct amdisplay_softc *sc)
+{
+       bus_size_t bsize;
+
+       bsize = (sc->sc_active_mode->hdisplay * sc->sc_active_mode->vdisplay
+             * sc->sc_active_depth) >> 3;
+       sc->sc_fb_size = bsize;
+       sc->sc_fb_dma_nsegs = 1;
+
+       if (bus_dmamap_create(sc->sc_dmat, sc->sc_fb_size, sc->sc_fb_dma_nsegs,
+           sc->sc_fb_size, 0, BUS_DMA_NOWAIT, &(sc->sc_fb0_dma)))
+               return -1;
+
+       if (bus_dmamem_alloc(sc->sc_dmat, sc->sc_fb_size, 4, 0,
+           sc->sc_fb0_dma_segs, 1, &(sc->sc_fb_dma_nsegs),
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT))
+               return -2;
+
+       if (bus_dmamem_map(sc->sc_dmat, sc->sc_fb0_dma_segs, 
sc->sc_fb_dma_nsegs,
+           sc->sc_fb_size, (caddr_t *)&(sc->sc_fb0),
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE))
+               return -3;
+
+       if (bus_dmamap_load(sc->sc_dmat, sc->sc_fb0_dma, sc->sc_fb0, bsize, 
NULL,
+           BUS_DMA_NOWAIT))
+               return -4;
+
+       memset(sc->sc_fb0, 0, bsize);
+
+       bus_dmamap_sync(sc->sc_dmat, sc->sc_fb0_dma, 0, bsize,
+           BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+       return 0;
+}
+
+void
+amdisplay_conf_crt_timings(struct amdisplay_softc *sc)
+{
+       uint32_t timing0, timing1, timing2;
+       uint32_t hbp, hfp, hsw, vbp, vfp, vsw, width, height;
+       struct videomode *m = sc->sc_active_mode;
+
+       timing0 = 0;
+       timing1 = 0;
+       timing2 = 0;
+
+       hbp = (m->htotal - m->hsync_end) - 1;
+       hfp = (m->hsync_start - m->hdisplay) - 1;
+       hsw = (m->hsync_end - m->hsync_start) - 1;
+
+       vbp = (m->vtotal - m->vsync_end);
+       vfp = (m->vsync_start - m->vdisplay);
+       vsw = (m->vsync_end - m->vsync_start) - 1;
+       
+       height = m->vdisplay - 1;
+       width  = m->hdisplay - 1;
+
+       /* Horizontal back porch */
+       timing0 |= (hbp & 0xff) << LCD_RASTER_TIMING_0_HBP_SHAMT;
+       timing2 |= ((hbp >> 8) & 3) << LCD_RASTER_TIMING_2_HPB_HIGHBITS_SHAMT;
+       /* Horizontal front porch */
+       timing0 |= (hfp & 0xff) << LCD_RASTER_TIMING_0_HFP_SHAMT;
+       timing2 |= ((hfp >> 8) & 3) << 0;
+       /* Horizontal sync width */
+       timing0 |= (hsw & 0x3f) << LCD_RASTER_TIMING_0_HSW_SHAMT;
+       timing2 |= ((hsw >> 6) & 0xf) << LCD_RASTER_TIMING_2_HSW_HIGHBITS_SHAMT;
+
+       /* Vertical back porch, front porch, sync width */
+       timing1 |= (vbp & 0xff) << LCD_RASTER_TIMING_1_VBP_SHAMT;
+       timing1 |= (vfp & 0xff) << LCD_RASTER_TIMING_1_VFP_SHAMT;
+       timing1 |= (vsw & 0x3f) << LCD_RASTER_TIMING_1_VSW_SHAMT;
+
+       /* Pixels per line */
+       timing0 |= ((width >> 10) & 1)
+           << LCD_RASTER_TIMING_0_PPLMSB_SHAMT;
+       timing0 |= ((width >> 4) & 0x3f)
+           << LCD_RASTER_TIMING_0_PPLLSB_SHAMT;
+
+       /* Lines per panel */
+       timing1 |= (height & 0x3ff);
+       timing2 |= ((height >> 10 ) & 1) 
+           << LCD_RASTER_TIMING_2_LPP_B10_SHAMT;
+
+       /* waveform settings */
+       timing2 |= LCD_RASTER_TIMING_2_PHSVS_ON_OFF;
+       timing2 |= (0xff << LCD_RASTER_TIMING_2_ACBI_SHAMT);
+
+       if (!ISSET(m->flags, VID_NHSYNC))
+           timing2 |= LCD_RASTER_TIMING_2_IHS;
+       if (!ISSET(m->flags, VID_NVSYNC))
+           timing2 |= LCD_RASTER_TIMING_2_IVS;
+
+       HWRITE4(sc, LCD_RASTER_TIMING_0, timing0);
+       HWRITE4(sc, LCD_RASTER_TIMING_1, timing1);
+       HWRITE4(sc, LCD_RASTER_TIMING_2, timing2);
+}
+
+int
+amdisplay_ioctl(void *sconf, u_long cmd, caddr_t data, int flat, struct proc 
*p)
+{
+       return -1;
+}
+
+paddr_t
+amdisplay_mmap(void *sconf, off_t off, int prot)
+{
+       return -1;
+}
+
+int
+amdisplay_alloc_screen(void *sconf, const struct wsscreen_descr *type,
+       void **cookiep, int *curxp, int *curyp, long *attrp)
+{
+       struct amdisplay_softc *sc = sconf;
+       struct rasops_info *ri = sc->sc_ro;
+
+       return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp);
+}
+
+void
+amdisplay_free_screen(void *sconf, void *cookie)
+{
+       struct amdisplay_softc *sc = sconf;
+       struct rasops_info *ri = sc->sc_ro;
+
+       return rasops_free_screen(ri, cookie);
+}
+
+int
+amdisplay_show_screen(void *sconf, void *cookie, int waitok,
+       void (*cb)(void *, int, int), void *cbarg)
+{
+       return (0);
+}
+
+void
+amdisplay_doswitch(void *v, void *dummy)
+{
+}
+
+int
+amdisplay_getchar(void *sconf, int row, int col, struct wsdisplay_charcell 
*cell)
+{
+       struct amdisplay_softc *sc = sconf;
+       struct rasops_info *ri = sc->sc_ro;
+
+       return rasops_getchar(ri, row, col, cell);
+}
+
+int
+amdisplay_load_font(void *sconf, void *cookie, struct wsdisplay_font *font)
+{
+       struct amdisplay_softc *sc = sconf;
+       struct rasops_info *ri = sc->sc_ro;
+
+       return rasops_load_font(ri, cookie, font);
+}
+
+int
+amdisplay_list_font(void *sconf, struct wsdisplay_font *font)
+{
+       struct amdisplay_softc *sc = sconf;
+       struct rasops_info *ri = sc->sc_ro;
+
+       return rasops_list_font(ri, font);
+}
+
+void
+amdisplay_burner(void *sconf, u_int on, u_int flags)
+{
+}
+
Index: sys/arch/armv7/omap/amdisplayreg.h
===================================================================
RCS file: sys/arch/armv7/omap/amdisplayreg.h
diff -N sys/arch/armv7/omap/amdisplayreg.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/armv7/omap/amdisplayreg.h  3 Apr 2017 05:24:33 -0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <i...@kremlin.cc>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* AM335x LCDC register offsets */
+#define LCD_PID                                        0x00
+#define LCD_CTRL                               0x04
+#define   LCD_CTRL_CLKDIV                      (0xFF << 8)
+#define          LCD_CTRL_AUTO_UFLOW_RESTART           (1 << 1)
+#define   LCD_CTRL_MODESEL                     (1 << 0)
+#define   LCD_CTRL_CLKDIV_SHAMT                        8
+#define LCD_RASTER_CTRL                                0x28
+#define   LCD_RASTER_CTRL_TFT24UNPACKED                (1 << 26)
+#define   LCD_RASTER_CTRL_TFT24                        (1 << 25)
+#define          LCD_RASTER_CTRL_STN565                (1 << 24)
+#define   LCD_RASTER_CTRL_TFTMAP               (1 << 23)
+#define   LCD_RASTER_CTRL_NIBMODE              (1 << 22)
+#define   LCD_RASTER_CTRL_PALMODE              (3 << 20)
+#define   LCD_RASTER_CTRL_REQDLY               (0xFF << 12)
+#define   LCD_RASTER_CTRL_LCDTFT               (1 << 7)
+#define   LCD_RASTER_CTRL_LCDEN                        (1 << 0)
+#define   LCD_RASTER_CTRL_PALMODE_SHAMT                20
+#define   LCD_RASTER_CTRL_REQDLY_SHAMT         12
+#define LCD_RASTER_TIMING_0                    0x2C
+#define   LCD_RASTER_TIMING_0_HBP              (0xFF << 24)
+#define   LCD_RASTER_TIMING_0_HFP              (0xFF << 16)
+#define   LCD_RASTER_TIMING_0_HSW              (0x3F << 10)
+#define   LCD_RASTER_TIMING_0_PPLLSB           (0x3  <<  4)
+#define   LCD_RASTER_TIMING_0_PPLMSB           (0x1  <<  3)
+#define   LCD_RASTER_TIMING_0_HBP_SHAMT                24
+#define   LCD_RASTER_TIMING_0_HFP_SHAMT                16
+#define   LCD_RASTER_TIMING_0_HSW_SHAMT                10
+#define   LCD_RASTER_TIMING_0_PPLLSB_SHAMT     4
+#define   LCD_RASTER_TIMING_0_PPLMSB_SHAMT     3
+#define LCD_RASTER_TIMING_1                    0x30
+#define   LCD_RASTER_TIMING_1_VBP              (0xFF  << 24)
+#define   LCD_RASTER_TIMING_1_VFP              (0xFF  << 16)
+#define   LCD_RASTER_TIMING_1_VSW              (0x3C  << 10)
+#define   LCD_RASTER_TIMING_1_LPP              (0x3FF <<  0)
+#define   LCD_RASTER_TIMING_1_VBP_SHAMT                24
+#define   LCD_RASTER_TIMING_1_VFP_SHAMT                16
+#define   LCD_RASTER_TIMING_1_VSW_SHAMT                10
+#define LCD_RASTER_TIMING_2                    0x34
+#define   LCD_RASTER_TIMING_2_HSW_HIGHBITS     (0xF  << 27)
+#define   LCD_RASTER_TIMING_2_LPP_B10          (0x1  << 26)
+#define   LCD_RASTER_TIMING_2_PHSVS_ON_OFF     (0x1  << 25)
+#define   LCD_RASTER_TIMING_2_PHSVS_RF         (0x1  << 24)
+#define   LCD_RASTER_TIMING_2_IEO              (0x1  << 23)
+#define   LCD_RASTER_TIMING_2_IPC              (0x1  << 22)
+#define   LCD_RASTER_TIMING_2_IHS              (0x1  << 21)
+#define   LCD_RASTER_TIMING_2_IVS              (0x1  << 20)
+#define   LCD_RASTER_TIMING_2_ACBI             (0xF  << 16)
+#define   LCD_RASTER_TIMING_2_ACB              (0xFF <<  8)
+#define   LCD_RASTER_TIMING_2_HBP_HIGHBITS     (0x3  <<  4)
+#define   LCD_RASTER_TIMING_2_HFP_HIGHBITS     (0x3  <<  0)
+#define   LCD_RASTER_TIMING_2_HSW_HIGHBITS_SHAMT       27
+#define   LCD_RASTER_TIMING_2_LPP_B10_SHAMT    26
+#define   LCD_RASTER_TIMING_2_ACBI_SHAMT       16
+#define   LCD_RASTER_TIMING_2_ACB_SHAMT                8
+#define   LCD_RASTER_TIMING_2_HPB_HIGHBITS_SHAMT       4
+#define LCD_RASTER_SUBPANEL                    0x38
+#define   LCD_RASTER_SUBPANEL_SPEN             (0x1    << 31)
+#define   LCD_RASTER_SUBPANEL_HOLS             (0x1    << 29)
+#define   LCD_RASTER_SUBPANEL_LPPT             (0x2FF  << 16)
+#define   LCD_RASTER_SUBPANEL_DPDLSB           (0xFFFF <<  0)
+#define   LCD_RASTER_SUBPANEL_LPPT_SHAMT       
+#define LCD_RASTER_SUBPANEL_2                  0x3C
+#define   LCD_RASTER_SUBPANEL2_LPPT_B10                (0x1  << 8)
+#define   LCD_RASTER_SUBPANEL2_DPDMSB          (0xFF << 0)
+#define LCD_LCDDMA_CTRL                                0x40
+#define   LCD_LCDDMA_CTRL_DMA_MASTER_PRIO      (0x7 << 0x10)
+#define   LCD_LCDDMA_CTRL_TH_FIFO_READY                (0x7 << 0x08)
+#define   LCD_LCDDMA_CTRL_BURST_SIZE           (0x7 << 0x04)
+#define   LCD_LCDDMA_CTRL_BYTE_SWAP            (0x1 << 0x03)
+#define   LCD_LCDDMA_CTRL_BIGENDIAN            (0x1 << 0x01)
+#define   LCD_LCDDMA_CTRL_FRAME_MODE           (0x1 << 0x00)
+#define   LCD_LCDDMA_CTRL_DMA_MASTER_PRIO_SHAMT        0xFF
+#define   LCD_LCDDMA_CTRL_TH_FIFO_READY_SHAMT  0x08
+#define   LCD_LCDDMA_CTRL_BURST_SIZE_SHAMT     0x04
+#define LCD_LCDDMA_FB0                         0x44
+#define   LCD_LCDDMA_FB0_BASE                  0xFFFC
+#define   LCD_LCDDMA_FB0_BASE_SHAMT            0
+#define LCD_LCDDMA_FB0_CEIL                    0x48
+#define   LCD_LCDDMA_FB0_CEILING               0xFFFC
+#define   LCD_LCDDMA_FB0_CEILING_SHAMT         0
+#define LCD_LCDDMA_FB1                         0x4C
+#define   LCD_LCDDMA_FB1_BASE                  0xFFFC
+#define   LCD_LCDDMA_FB1_BASE_SHAMT            0
+#define LCD_LCDDMA_FB1_CEIL                    0x50
+#define   LCD_LCDDMA_FB1_CEILING               0xFFFC
+#define   LCD_LCDDMA_FB1_CEILING_SHAMT         0
+#define LCD_SYSCONFIG                          0x54
+#define   LCD_SYSCONFIG_STANDBYMODE            (2 << 4)
+#define   LCD_SYSCONFIG_IDLEMODE               (2 << 2)
+#define   LCD_SYSCONFIG_STANDBYMODE_SHAMT      4
+#define   LCD_SYSCONFIG_IDLEMODE_SHAMT         2
+#define LCD_IRQSTATUS_RAW                      0x58
+#define LCD_IRQSTATUS                          0x5C
+#define LCD_IRQENABLE_SET                      0x60
+#define LCD_IRQENABLE_CLEAR                    0x64
+#define LCD_IRQ_END                            0x68
+#define LCD_CLKC_ENABLE                                0x6C
+#define   LCD_CLKC_ENABLE_DMA_CLK_EN           (1 << 2)
+#define   LCD_CLKC_ENABLE_LIDD_CLK_EN          (1 << 1)
+#define   LCD_CLKC_ENABLE_CORE_CLK_EN          (1 << 0)
+#define LCD_CLKC_RESET                         0x70
+#define   LCD_CLKC_RESET_MAIN_RST              (1 << 3)
+#define   LCD_CLKC_RESET_DMA_RST               (1 << 2)
+#define   LCD_CLKC_RESET_LIDD_RST              (1 << 1)
+#define   LCD_CLKC_RESET_CORE_RST              (1 << 0)
+
+/* AM335x LCDC intr. masks */
+#define LCD_IRQ_EOF1   (1 << 9)
+#define LCD_IRQ_EOF0   (1 << 8)
+#define LCD_IRQ_PL     (1 << 6)
+#define LCD_IRQ_FUF    (1 << 5)
+#define LCD_IRQ_ACB    (1 << 3)
+#define LCD_IRQ_SYNC   (1 << 2)
+#define LCD_IRQ_RR_DONE        (1 << 1)
+#define LCD_IRQ_DONE   (1 << 0)
+
+/* EDID reading */ 
+#define EDID_LENGTH    0x80
+
+/* phandle for pin muxing */
+#define LCD_FDT_PHANDLE 0x2f
Index: sys/arch/armv7/omap/files.omap
===================================================================
RCS file: /cvs/src/sys/arch/armv7/omap/files.omap,v
retrieving revision 1.19
diff -u -p -p -u -r1.19 files.omap
--- sys/arch/armv7/omap/files.omap      3 Oct 2016 01:59:20 -0000       1.19
+++ sys/arch/armv7/omap/files.omap      3 Apr 2017 05:24:33 -0000
@@ -86,6 +86,10 @@ device omdisplay: wsemuldisplaydev, raso
 attach omdisplay at omap
 file arch/armv7/omap/omdisplay.c               omdisplay
 
+device amdisplay: wsemuldisplaydev, rasops16
+attach amdisplay at fdt
+file arch/armv7/omap/amdisplay.c               amdisplay
+
 # MCSPI - spi 
 device mcspi 
 attach mcspi at omap
@@ -95,3 +99,9 @@ file  arch/armv7/omap/mcspi.c                 mcspi
 device oaudio: audio
 attach oaudio at omap                          # configure after Atlas Driver
 file arch/armv7/omap/beagle_audio.c            oaudio
+
+# TDA19988 HDMI PHY
+device nxptda
+attach nxptda at i2c
+file   arch/armv7/omap/nxptda.c                nxptda
+
Index: sys/arch/armv7/omap/nxptda.c
===================================================================
RCS file: sys/arch/armv7/omap/nxptda.c
diff -N sys/arch/armv7/omap/nxptda.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/armv7/omap/nxptda.c        3 Apr 2017 05:24:34 -0000
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <i...@ce.gl>
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/errno.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/videomode/videomode.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pinctrl.h>
+
+#include <arch/armv7/omap/nxptdavar.h>
+
+/* TDA19988 registers */
+#define        MKREG(page, addr)       (((page) << 8) | (addr))
+
+#define        REGPAGE(reg)            (((reg) >> 8) & 0xff)
+#define        REGADDR(reg)            ((reg) & 0xff)
+
+#define TDA_VERSION            MKREG(0x00, 0x00)
+#define TDA_MAIN_CNTRL0                MKREG(0x00, 0x01)
+#define        MAIN_CNTRL0_SR          (1 << 0)
+#define TDA_VERSION_MSB                MKREG(0x00, 0x02)
+#define        TDA_SOFTRESET           MKREG(0x00, 0x0a)
+#define                SOFTRESET_I2C           (1 << 1)
+#define                SOFTRESET_AUDIO         (1 << 0)
+#define        TDA_DDC_CTRL            MKREG(0x00, 0x0b)
+#define                DDC_ENABLE              0
+#define        TDA_CCLK                MKREG(0x00, 0x0c)
+#define                CCLK_ENABLE             1
+#define        TDA_INT_FLAGS_2         MKREG(0x00, 0x11)
+#define                INT_FLAGS_2_EDID_BLK_RD (1 << 1)
+
+#define        TDA_VIP_CNTRL_0         MKREG(0x00, 0x20)
+#define        TDA_VIP_CNTRL_1         MKREG(0x00, 0x21)
+#define        TDA_VIP_CNTRL_2         MKREG(0x00, 0x22)
+#define        TDA_VIP_CNTRL_3         MKREG(0x00, 0x23)
+#define                VIP_CNTRL_3_SYNC_HS     (2 << 4)
+#define                VIP_CNTRL_3_V_TGL       (1 << 2)
+#define                VIP_CNTRL_3_H_TGL       (1 << 1)
+
+#define        TDA_VIP_CNTRL_4         MKREG(0x00, 0x24)
+#define                VIP_CNTRL_4_BLANKIT_NDE         (0 << 2)
+#define                VIP_CNTRL_4_BLANKIT_HS_VS       (1 << 2)
+#define                VIP_CNTRL_4_BLANKIT_NHS_VS      (2 << 2)
+#define                VIP_CNTRL_4_BLANKIT_HE_VE       (3 << 2)
+#define                VIP_CNTRL_4_BLC_NONE            (0 << 0)
+#define                VIP_CNTRL_4_BLC_RGB444          (1 << 0)
+#define                VIP_CNTRL_4_BLC_YUV444          (2 << 0)
+#define                VIP_CNTRL_4_BLC_YUV422          (3 << 0)
+#define        TDA_VIP_CNTRL_5         MKREG(0x00, 0x25)
+#define                VIP_CNTRL_5_SP_CNT(n)   (((n) & 3) << 1)
+#define        TDA_MUX_VP_VIP_OUT      MKREG(0x00, 0x27)
+#define TDA_MAT_CONTRL         MKREG(0x00, 0x80)
+#define                MAT_CONTRL_MAT_BP       (1 << 2)
+#define        TDA_VIDFORMAT           MKREG(0x00, 0xa0)
+#define        TDA_REFPIX_MSB          MKREG(0x00, 0xa1)
+#define        TDA_REFPIX_LSB          MKREG(0x00, 0xa2)
+#define        TDA_REFLINE_MSB         MKREG(0x00, 0xa3)
+#define        TDA_REFLINE_LSB         MKREG(0x00, 0xa4)
+#define        TDA_NPIX_MSB            MKREG(0x00, 0xa5)
+#define        TDA_NPIX_LSB            MKREG(0x00, 0xa6)
+#define        TDA_NLINE_MSB           MKREG(0x00, 0xa7)
+#define        TDA_NLINE_LSB           MKREG(0x00, 0xa8)
+#define        TDA_VS_LINE_STRT_1_MSB  MKREG(0x00, 0xa9)
+#define        TDA_VS_LINE_STRT_1_LSB  MKREG(0x00, 0xaa)
+#define        TDA_VS_PIX_STRT_1_MSB   MKREG(0x00, 0xab)
+#define        TDA_VS_PIX_STRT_1_LSB   MKREG(0x00, 0xac)
+#define        TDA_VS_LINE_END_1_MSB   MKREG(0x00, 0xad)
+#define        TDA_VS_LINE_END_1_LSB   MKREG(0x00, 0xae)
+#define        TDA_VS_PIX_END_1_MSB    MKREG(0x00, 0xaf)
+#define        TDA_VS_PIX_END_1_LSB    MKREG(0x00, 0xb0)
+#define        TDA_VS_LINE_STRT_2_MSB  MKREG(0x00, 0xb1)
+#define        TDA_VS_LINE_STRT_2_LSB  MKREG(0x00, 0xb2)
+#define        TDA_VS_PIX_STRT_2_MSB   MKREG(0x00, 0xb3)
+#define        TDA_VS_PIX_STRT_2_LSB   MKREG(0x00, 0xb4)
+#define        TDA_VS_LINE_END_2_MSB   MKREG(0x00, 0xb5)
+#define        TDA_VS_LINE_END_2_LSB   MKREG(0x00, 0xb6)
+#define        TDA_VS_PIX_END_2_MSB    MKREG(0x00, 0xb7)
+#define        TDA_VS_PIX_END_2_LSB    MKREG(0x00, 0xb8)
+#define        TDA_HS_PIX_START_MSB    MKREG(0x00, 0xb9)
+#define        TDA_HS_PIX_START_LSB    MKREG(0x00, 0xba)
+#define        TDA_HS_PIX_STOP_MSB     MKREG(0x00, 0xbb)
+#define        TDA_HS_PIX_STOP_LSB     MKREG(0x00, 0xbc)
+#define        TDA_VWIN_START_1_MSB    MKREG(0x00, 0xbd)
+#define        TDA_VWIN_START_1_LSB    MKREG(0x00, 0xbe)
+#define        TDA_VWIN_END_1_MSB      MKREG(0x00, 0xbf)
+#define        TDA_VWIN_END_1_LSB      MKREG(0x00, 0xc0)
+#define        TDA_VWIN_START_2_MSB    MKREG(0x00, 0xc1)
+#define        TDA_VWIN_START_2_LSB    MKREG(0x00, 0xc2)
+#define        TDA_VWIN_END_2_MSB      MKREG(0x00, 0xc3)
+#define        TDA_VWIN_END_2_LSB      MKREG(0x00, 0xc4)
+#define        TDA_DE_START_MSB        MKREG(0x00, 0xc5)
+#define        TDA_DE_START_LSB        MKREG(0x00, 0xc6)
+#define        TDA_DE_STOP_MSB         MKREG(0x00, 0xc7)
+#define        TDA_DE_STOP_LSB         MKREG(0x00, 0xc8)
+
+#define        TDA_TBG_CNTRL_0         MKREG(0x00, 0xca)
+#define                TBG_CNTRL_0_SYNC_ONCE   (1 << 7)
+#define                TBG_CNTRL_0_SYNC_MTHD   (1 << 6)
+
+#define        TDA_TBG_CNTRL_1         MKREG(0x00, 0xcb)
+#define                TBG_CNTRL_1_DWIN_DIS    (1 << 6)
+#define                TBG_CNTRL_1_TGL_EN      (1 << 2)
+#define                TBG_CNTRL_1_V_TGL       (1 << 1)
+#define                TBG_CNTRL_1_H_TGL       (1 << 0)
+
+#define        TDA_HVF_CNTRL_0         MKREG(0x00, 0xe4)
+#define                HVF_CNTRL_0_PREFIL_NONE         (0 << 2)
+#define                HVF_CNTRL_0_INTPOL_BYPASS       (0 << 0)
+#define        TDA_HVF_CNTRL_1         MKREG(0x00, 0xe5)
+#define                HVF_CNTRL_1_VQR(x)      (((x) & 3) << 2)
+#define                HVF_CNTRL_1_VQR_FULL    HVF_CNTRL_1_VQR(0)
+#define        TDA_ENABLE_SPACE        MKREG(0x00, 0xd6)
+#define        TDA_RPT_CNTRL           MKREG(0x00, 0xf0)
+
+#define        TDA_PLL_SERIAL_1        MKREG(0x02, 0x00)
+#define                PLL_SERIAL_1_SRL_MAN_IP (1 << 6)
+#define        TDA_PLL_SERIAL_2        MKREG(0x02, 0x01)
+#define                PLL_SERIAL_2_SRL_PR(x)          (((x) & 0xf) << 4)
+#define                PLL_SERIAL_2_SRL_NOSC(x)        (((x) & 0x3) << 0)
+#define        TDA_PLL_SERIAL_3        MKREG(0x02, 0x02)
+#define                PLL_SERIAL_3_SRL_PXIN_SEL       (1 << 4)
+#define                PLL_SERIAL_3_SRL_DE             (1 << 2)
+#define                PLL_SERIAL_3_SRL_CCIR           (1 << 0)
+#define        TDA_SERIALIZER          MKREG(0x02, 0x03)
+#define        TDA_BUFFER_OUT          MKREG(0x02, 0x04)
+#define        TDA_PLL_SCG1            MKREG(0x02, 0x05)
+#define        TDA_PLL_SCG2            MKREG(0x02, 0x06)
+#define        TDA_PLL_SCGN1           MKREG(0x02, 0x07)
+#define        TDA_PLL_SCGN2           MKREG(0x02, 0x08)
+#define        TDA_PLL_SCGR1           MKREG(0x02, 0x09)
+#define        TDA_PLL_SCGR2           MKREG(0x02, 0x0a)
+
+#define        TDA_SEL_CLK             MKREG(0x02, 0x11)
+#define                SEL_CLK_ENA_SC_CLK      (1 << 3)
+#define                SEL_CLK_SEL_VRF_CLK(x)  (((x) & 3) << 1)
+#define                SEL_CLK_SEL_CLK1        (1 << 0)
+#define        TDA_ANA_GENERAL         MKREG(0x02, 0x12)
+
+#define        TDA_EDID_DATA0          MKREG(0x09, 0x00)
+#define        TDA_EDID_CTRL           MKREG(0x09, 0xfa)
+#define        TDA_DDC_ADDR            MKREG(0x09, 0xfb)
+#define        TDA_DDC_OFFS            MKREG(0x09, 0xfc)
+#define        TDA_DDC_SEGM_ADDR       MKREG(0x09, 0xfd)
+#define        TDA_DDC_SEGM            MKREG(0x09, 0xfe)
+
+#define        TDA_IF_VSP              MKREG(0x10, 0x20)
+#define        TDA_IF_AVI              MKREG(0x10, 0x40)
+#define        TDA_IF_SPD              MKREG(0x10, 0x60)
+#define        TDA_IF_AUD              MKREG(0x10, 0x80)
+#define        TDA_IF_MPS              MKREG(0x10, 0xa0)
+
+#define        TDA_ENC_CNTRL           MKREG(0x11, 0x0d)
+#define                ENC_CNTRL_DVI_MODE      (0 << 2)
+#define                ENC_CNTRL_HDMI_MODE     (1 << 2)
+#define        TDA_DIP_IF_FLAGS        MKREG(0x11, 0x0f)
+#define                DIP_IF_FLAGS_IF5        (1 << 5)
+#define                DIP_IF_FLAGS_IF4        (1 << 4)
+#define                DIP_IF_FLAGS_IF3        (1 << 3)
+#define                DIP_IF_FLAGS_IF2        (1 << 2) /*  AVI IF on page 10h 
*/
+#define                DIP_IF_FLAGS_IF1        (1 << 1)
+
+#define        TDA_TX3                 MKREG(0x12, 0x9a)
+#define        TDA_TX4                 MKREG(0x12, 0x9b)
+#define                TX4_PD_RAM              (1 << 1)
+#define        TDA_HDCP_TX33           MKREG(0x12, 0xb8)
+#define                HDCP_TX33_HDMI          (1 << 1)
+
+#define        TDA_CURPAGE_ADDR        0xff
+
+#define        TDA_CEC_ENAMODS         0xff
+#define                ENAMODS_RXSENS          (1 << 2)
+#define                ENAMODS_HDMI            (1 << 1)
+#define        TDA_CEC_FRO_IM_CLK_CTRL 0xfb
+#define                CEC_FRO_IM_CLK_CTRL_GHOST_DIS   (1 << 7)
+#define                CEC_FRO_IM_CLK_CTRL_IMCLK_SEL   (1 << 1)
+
+/*  EDID reading */ 
+#define EDID_LENGTH            0x80
+#define        MAX_READ_ATTEMPTS       100
+
+/*  EDID fields */
+#define        EDID_MODES0             35
+#define        EDID_MODES1             36
+#define        EDID_TIMING_START       38
+#define        EDID_TIMING_END         54
+#define        EDID_TIMING_X(v)        (((v) + 31) * 8)
+#define        EDID_FREQ(v)            (((v) & 0x3f) + 60)
+#define        EDID_RATIO(v)           (((v) >> 6) & 0x3)
+#define        EDID_RATIO_10x16        0
+#define        EDID_RATIO_3x4          1       
+#define        EDID_RATIO_4x5          2       
+#define        EDID_RATIO_9x16         3
+
+/* NXP TDA19988 slave addrs. */
+#define TDA_HDMI               0x70
+#define TDA_CEC                        0x34
+
+/* debug/etc macros */
+#define DEVNAME(s)             ((s)->sc_dev.dv_xname)
+//#ifdef I2CDEBUG
+#ifdef NXPTDA_DEBUG
+int nxptda_debug = 20;
+#define DPRINTF(n,s)           do { if ((n) <= nxptda_debug) printf s; } while 
(0)
+#else
+#define DPRINTF(n,s)           do {} while (0)
+#endif
+
+struct nxptda_softc {
+       struct device   sc_dev;
+       i2c_tag_t       sc_tag;
+       i2c_addr_t      sc_addr;
+
+       uint8_t         sc_curpage;
+       uint8_t         sc_edid[EDID_LENGTH];
+};
+
+int    nxptda_match(struct device *, void *, void *);
+void   nxptda_attach(struct device *, struct device *, void *);
+
+int    nxptda_cec_read(struct nxptda_softc *, uint8_t, uint8_t *);
+int    nxptda_cec_write(struct nxptda_softc *, uint8_t, uint8_t);
+int    nxptda_read(struct nxptda_softc *, uint16_t, uint8_t *);
+int    nxptda_write(struct nxptda_softc *, uint16_t, uint8_t);
+int    nxptda_write2(struct nxptda_softc *, uint16_t, uint16_t);
+int    nxptda_set(struct nxptda_softc *, uint16_t, uint8_t);
+int    nxptda_clear(struct nxptda_softc *, uint16_t, uint8_t);
+int    nxptda_set_page(struct nxptda_softc *, uint8_t);
+int    nxptda_read_edid(struct nxptda_softc *);
+int    nxptda_reset(struct nxptda_softc *);
+int    nxptda_init_encoder(struct nxptda_softc *, struct videomode *);
+
+int    nxptda_get_edid(uint8_t *, int);
+int    nxptda_set_videomode(struct videomode *);
+
+struct cfattach nxptda_ca = {
+       sizeof(struct nxptda_softc), nxptda_match, nxptda_attach
+};
+
+struct cfdriver nxptda_cd = {
+       NULL, "nxptda", DV_DULL
+};
+
+int
+nxptda_match(struct device *parent, void *match, void *aux)
+{
+       struct i2c_attach_args *ia = aux;
+
+       if (strcmp(ia->ia_name, "nxp,tda998x") == 0)
+           return 1;
+
+       return 0;
+}
+
+void
+nxptda_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct nxptda_softc *sc = (struct nxptda_softc *)self;
+       struct i2c_attach_args *ia = aux;
+       uint8_t data = 0;
+       uint16_t version = 0;
+       int res = 0, node = *(int *)(ia->ia_cookie);
+
+       sc->sc_tag = ia->ia_tag;
+       sc->sc_addr = ia->ia_addr;
+       sc->sc_curpage = 0xff;
+
+       if (!node) {
+           printf(": not configured\n");
+           return;
+       } else if ((pinctrl_byname(node, "default") == -1)) {
+           printf(": not configured\n");
+           return;
+       }
+
+       iic_acquire_bus(sc->sc_tag, 0);
+
+       DPRINTF(3,("\n"));
+
+       /* enable HDMI core */
+       nxptda_cec_write(sc, TDA_CEC_ENAMODS, ENAMODS_RXSENS | ENAMODS_HDMI);
+       delay(1000);
+
+       if (!(nxptda_reset(sc)))
+           DPRINTF(3,("%s: software reset OK\n", DEVNAME(sc)));
+       else
+           DPRINTF(3,("%s: software reset failed!\n", DEVNAME(sc)));
+
+       /*  PLL registers common configuration */
+       nxptda_write(sc, TDA_PLL_SERIAL_1, 0x00);
+       nxptda_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1));
+       nxptda_write(sc, TDA_PLL_SERIAL_3, 0x00);
+       nxptda_write(sc, TDA_SERIALIZER, 0x00);
+       nxptda_write(sc, TDA_BUFFER_OUT, 0x00);
+       nxptda_write(sc, TDA_PLL_SCG1, 0x00);
+       nxptda_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+       nxptda_write(sc, TDA_PLL_SCGN1, 0xfa);
+       nxptda_write(sc, TDA_PLL_SCGN2, 0x00);
+       nxptda_write(sc, TDA_PLL_SCGR1, 0x5b);
+       nxptda_write(sc, TDA_PLL_SCGR2, 0x00);
+       nxptda_write(sc, TDA_PLL_SCG2, 0x10);
+
+       /*  Write the default value MUX register */
+       nxptda_write(sc, TDA_MUX_VP_VIP_OUT, 0x24);
+
+       res |= nxptda_read(sc, TDA_VERSION, &data);
+       version |= data;
+       res |= nxptda_read(sc, TDA_VERSION_MSB, &data);
+       version |= (data << 8);
+       version &= ~0x30;
+
+       if (!res) {
+           DPRINTF(3,("%s: ", DEVNAME(sc)));
+           printf(": rev 0x%04x\n", version);
+       } else {
+           DPRINTF(3,("%s: ", DEVNAME(sc)));
+           printf(": failed to enable HDMI core, exiting...\n");
+           iic_release_bus(sc->sc_tag, 0);
+           return;
+       }
+
+       nxptda_write(sc, TDA_DDC_CTRL, DDC_ENABLE);
+       nxptda_write(sc, TDA_TX3, 39);
+
+       nxptda_cec_write(sc, TDA_CEC_FRO_IM_CLK_CTRL,
+           CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
+
+       if (nxptda_read_edid(sc)) {
+           DPRINTF(3,("%s: failed to read EDID bits, exiting!\n", 
DEVNAME(sc)));
+           return;
+       }
+
+       /*  Default values for RGB 4:4:4 mapping */
+       nxptda_write(sc, TDA_VIP_CNTRL_0, 0x23);
+       nxptda_write(sc, TDA_VIP_CNTRL_1, 0x01);
+       nxptda_write(sc, TDA_VIP_CNTRL_2, 0x45);
+
+       iic_release_bus(sc->sc_tag, 0);
+}
+
+int
+nxptda_cec_read(struct nxptda_softc *sc, uint8_t addr, uint8_t *buf)
+{
+       int ret = 0;
+
+       if ((ret |= iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, TDA_CEC, &addr, 
1,
+           NULL, 0, 0))) {
+               DPRINTF(3,("%s: (CEC) failed to read addr 0x%02x, errno %d\n", 
DEVNAME(sc),
+                   addr, ret));
+               return ret;
+       }
+
+       DPRINTF(3,("%s: (CEC) read 0x%02x from 0x%02x\n", DEVNAME(sc), *buf, 
addr));
+
+       return ret;
+}
+
+int
+nxptda_cec_write(struct nxptda_softc *sc, uint8_t addr, uint8_t val)
+{
+       int ret = 0;
+       uint8_t sendbuf[] = { addr, val };
+
+       if ((ret |= iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_CEC, 
&sendbuf, 2,
+           NULL, 0, 0))) {
+               DPRINTF(3,("%s: (CEC) failed to write 0x%02x to 0x%02x, errno 
%d\n",
+                   DEVNAME(sc), val, addr, ret));
+               return ret;
+       }
+
+       DPRINTF(3,("%s: (CEC) wrote 0x%02x to 0x%02x\n", DEVNAME(sc), val, 
addr));
+
+       return ret;
+}
+
+int
+nxptda_read(struct nxptda_softc *sc, uint16_t reg, uint8_t *buf)
+{
+       int ret = 0;
+
+       nxptda_set_page(sc, REGPAGE(reg));
+
+       if ((ret = iic_smbus_read_byte(sc->sc_tag, TDA_HDMI, REGADDR(reg), buf, 
0))) {
+           DPRINTF(3,("%s: failed to read addr 0x%02x on page 0x%02x, errno 
%d\n",
+               DEVNAME(sc), REGADDR(reg), REGPAGE(reg), ret));
+           return ret;
+       }
+
+       DPRINTF(3,("%s: read  0x%02x from 0x%02x on page 0x%02x\n", 
DEVNAME(sc), *buf,
+           REGADDR(reg), REGPAGE(reg)));
+
+       return ret;
+}
+
+int
+nxptda_write(struct nxptda_softc *sc, uint16_t reg, uint8_t val)
+{
+       int ret = 0;
+       uint8_t sendbuf[] = { REGADDR(reg), val };
+
+       nxptda_set_page(sc, REGPAGE(reg));
+
+       if ((ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_HDMI, 
&sendbuf, 2,
+           NULL, 0, 0))) {
+               DPRINTF(3,("%s: failed to write 0x%02x to 0x%02x on page 
0x%02x, errno %d\n",
+                   DEVNAME(sc), val, REGADDR(reg), REGPAGE(reg), ret));
+               return ret;
+       }
+
+       DPRINTF(3,("%s: wrote 0x%02x  to  0x%02x on page 0x%02x\n", 
DEVNAME(sc), val,
+           REGADDR(reg), REGPAGE(reg)));
+
+       return ret;
+}
+
+int
+nxptda_write2(struct nxptda_softc *sc, uint16_t reg, uint16_t val)
+{
+       int ret = 0;
+       uint8_t sendbuf[] = { REGADDR(reg), val >> 8, val & 0xff };
+
+       nxptda_set_page(sc, REGPAGE(reg));
+
+       if ((ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_HDMI, 
&sendbuf, 3,
+           NULL, 0, 0))) {
+               DPRINTF(3,("%s: failed to write 0x%04x to 0x%02x on page 
0x%02x, errno %d\n",
+                   DEVNAME(sc), val, REGADDR(reg), REGPAGE(reg), ret));
+               return ret;
+       }
+
+       DPRINTF(3,("%s: wrote 0x%04x  to  0x%02x on page 0x%02x\n", 
DEVNAME(sc), val,
+           REGADDR(reg), REGPAGE(reg)));
+
+       return ret;
+
+}
+
+int
+nxptda_set(struct nxptda_softc *sc, uint16_t reg, uint8_t bits)
+{
+       int ret = 0;
+       uint8_t buf;
+
+       ret |= nxptda_read(sc, reg, &buf);
+       buf |= bits;
+       ret |= nxptda_write(sc, reg, buf);
+
+       return ret;
+}
+
+int
+nxptda_clear(struct nxptda_softc *sc, uint16_t reg, uint8_t bits)
+{
+       int ret = 0;
+       uint8_t buf;
+
+       ret |= nxptda_read(sc, reg, &buf);
+       buf &= ~bits;
+       ret |= nxptda_write(sc, reg, buf);
+
+       return ret;
+}
+
+int
+nxptda_set_page(struct nxptda_softc *sc, uint8_t page)
+{
+       int ret = 0;
+       uint8_t sendbuf[] = { TDA_CURPAGE_ADDR, page };
+
+       if (sc->sc_curpage == page)
+           return ret;
+
+       if ((ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, TDA_HDMI, 
&sendbuf,
+           sizeof(sendbuf), NULL, 0, 0))) {
+               DPRINTF(3,("%s: failed to set memory page 0x%02x, errno %d\n", 
DEVNAME(sc),
+               page, ret));
+               return ret;
+       }
+
+       sc->sc_curpage = page;
+       DPRINTF(3,("%s: set page to 0x%02x\n", DEVNAME(sc), page));
+
+       return ret;
+}
+
+int
+nxptda_read_edid(struct nxptda_softc *sc)
+{
+       int i = 0, ret = 0;
+       uint8_t reg;
+
+       nxptda_set(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+
+       /*  Block 0 */
+       nxptda_write(sc, TDA_DDC_ADDR, 0xa0);
+       nxptda_write(sc, TDA_DDC_OFFS, 0x00);
+       nxptda_write(sc, TDA_DDC_SEGM_ADDR, 0x60);
+       nxptda_write(sc, TDA_DDC_SEGM, 0x00);
+
+       nxptda_write(sc, TDA_EDID_CTRL, 1);
+       nxptda_write(sc, TDA_EDID_CTRL, 0);
+
+       for (; i < MAX_READ_ATTEMPTS; i++) {
+           nxptda_read(sc, TDA_INT_FLAGS_2, &reg);
+           if (reg & INT_FLAGS_2_EDID_BLK_RD) {
+               DPRINTF(3,("%s: EDID-ready IRQ fired\n", DEVNAME(sc)));
+               break;
+           }
+       }
+
+       if (i == MAX_READ_ATTEMPTS) {
+           printf("%s: no display detected\n", DEVNAME(sc));
+           ret = ENXIO;
+           return ret;
+       }
+
+       nxptda_set_page(sc, 0x09);
+
+       reg = 0x00;
+       DPRINTF(3,("%s: ------------- EDID -------------", DEVNAME(sc)));
+       for (i = 0; i < EDID_LENGTH; i++) {
+           iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, TDA_HDMI, &reg, 1,
+               &sc->sc_edid[i], 1, 0);
+           if (!(i % 16))
+               DPRINTF(3,("\n%s: ", DEVNAME(sc)));
+           DPRINTF(3,("%02x", sc->sc_edid[i]));
+           reg++;
+       }
+       DPRINTF(3,("\n%s: --------------------------------\n", DEVNAME(sc)));
+
+       nxptda_clear(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+       nxptda_set(sc, TDA_TX4, TX4_PD_RAM);
+
+       return ret;
+}
+
+int
+nxptda_reset(struct nxptda_softc *sc)
+{
+       int ret = 0;
+
+       /* reset core */
+       ret |= nxptda_set(sc, TDA_SOFTRESET, 3);
+       delay(100);
+       ret |= nxptda_clear(sc, TDA_SOFTRESET, 3);
+       delay(100);
+
+       /* reset transmitter */
+       ret |= nxptda_set(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+       ret |= nxptda_clear(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+
+       return ret;
+}
+
+int
+nxptda_init_encoder(struct nxptda_softc *sc, struct videomode *mode)
+{
+       int ret = 0;
+
+       uint16_t ref_pix, ref_line, n_pix, n_line;
+       uint16_t hs_pix_start, hs_pix_stop;
+       uint16_t vs1_pix_start, vs1_pix_stop;
+       uint16_t vs1_line_start, vs1_line_end;
+       uint16_t vs2_pix_start, vs2_pix_stop;
+       uint16_t vs2_line_start, vs2_line_end;
+       uint16_t vwin1_line_start, vwin1_line_end;
+       uint16_t vwin2_line_start, vwin2_line_end;
+       uint16_t de_start, de_stop;
+       uint8_t reg, div;
+
+       n_pix = mode->htotal;
+       n_line = mode->vtotal;
+
+       hs_pix_stop = mode->hsync_end - mode->hdisplay;
+       hs_pix_start = mode->hsync_start - mode->hdisplay;
+
+       de_stop = mode->htotal;
+       de_start = mode->htotal - mode->hdisplay;
+       ref_pix = hs_pix_start + 3;
+
+       if (mode->flags & VID_HSKEW)
+           ref_pix += mode->hsync_end - mode->hsync_start;
+
+       if ((mode->flags & VID_INTERLACE) == 0) {
+               ref_line = 1 + mode->vsync_start - mode->vdisplay;
+               vwin1_line_start = mode->vtotal - mode->vdisplay - 1;
+               vwin1_line_end = vwin1_line_start + mode->vdisplay;
+
+               vs1_pix_start = vs1_pix_stop = hs_pix_start;
+               vs1_line_start = mode->vsync_start - mode->vdisplay;
+               vs1_line_end = vs1_line_start + mode->vsync_end - 
mode->vsync_start;
+
+               vwin2_line_start = vwin2_line_end = 0;
+               vs2_pix_start = vs2_pix_stop = 0;
+               vs2_line_start = vs2_line_end = 0;
+       } else {
+               ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2;
+               vwin1_line_start = (mode->vtotal - mode->vdisplay)/2;
+               vwin1_line_end = vwin1_line_start + mode->vdisplay/2;
+
+               vs1_pix_start = vs1_pix_stop = hs_pix_start;
+               vs1_line_start = (mode->vsync_start - mode->vdisplay)/2;
+               vs1_line_end = vs1_line_start + (mode->vsync_end - 
mode->vsync_start)/2;
+
+               vwin2_line_start = vwin1_line_start + mode->vtotal/2;
+               vwin2_line_end = vwin2_line_start + mode->vdisplay/2;
+
+               vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal/2;
+               vs2_line_start = vs1_line_start + mode->vtotal/2 ;
+               vs2_line_end = vs2_line_start + (mode->vsync_end - 
mode->vsync_start)/2;
+       }
+
+       div = 148500 / mode->dot_clock;
+       if (div != 0) {
+               div--;
+               if (div > 3)
+                       div = 3;
+       }
+
+       /*  set HDMI HDCP mode off */
+       nxptda_set(sc, TDA_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
+       nxptda_clear(sc, TDA_HDCP_TX33, HDCP_TX33_HDMI);
+       nxptda_write(sc, TDA_ENC_CNTRL, ENC_CNTRL_DVI_MODE);
+
+       /*  no pre-filter or interpolator */
+       nxptda_write(sc, TDA_HVF_CNTRL_0,
+           HVF_CNTRL_0_INTPOL_BYPASS | HVF_CNTRL_0_PREFIL_NONE);
+       nxptda_write(sc, TDA_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
+       nxptda_write(sc, TDA_VIP_CNTRL_4,
+           VIP_CNTRL_4_BLANKIT_NDE | VIP_CNTRL_4_BLC_NONE);
+
+       nxptda_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR);
+       nxptda_clear(sc, TDA_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IP);
+       nxptda_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE);
+       nxptda_write(sc, TDA_SERIALIZER, 0);
+       nxptda_write(sc, TDA_HVF_CNTRL_1, HVF_CNTRL_1_VQR_FULL);
+
+       nxptda_write(sc, TDA_RPT_CNTRL, 0);
+       nxptda_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
+                       SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+
+       nxptda_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
+                       PLL_SERIAL_2_SRL_PR(0));
+
+       nxptda_set(sc, TDA_MAT_CONTRL, MAT_CONTRL_MAT_BP);
+
+       nxptda_write(sc, TDA_ANA_GENERAL, 0x09);
+
+       nxptda_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD);
+
+       /*
+        * Sync on rising HSYNC/VSYNC
+        */
+       reg = VIP_CNTRL_3_SYNC_HS;
+       if (mode->flags & VID_NHSYNC)
+               reg |= VIP_CNTRL_3_H_TGL;
+       if (mode->flags & VID_NVSYNC)
+               reg |= VIP_CNTRL_3_V_TGL;
+       nxptda_write(sc, TDA_VIP_CNTRL_3, reg);
+
+       reg = TBG_CNTRL_1_TGL_EN;
+       if (mode->flags & VID_NHSYNC)
+               reg |= TBG_CNTRL_1_H_TGL;
+       if (mode->flags & VID_NVSYNC)
+               reg |= TBG_CNTRL_1_V_TGL;
+       nxptda_write(sc, TDA_TBG_CNTRL_1, reg);
+
+       /*  Program timing */
+       nxptda_write(sc, TDA_VIDFORMAT, 0x00);
+
+       nxptda_write2(sc, TDA_REFPIX_MSB, ref_pix);
+       nxptda_write2(sc, TDA_REFLINE_MSB, ref_line);
+       nxptda_write2(sc, TDA_NPIX_MSB, n_pix);
+       nxptda_write2(sc, TDA_NLINE_MSB, n_line);
+
+       nxptda_write2(sc, TDA_VS_LINE_STRT_1_MSB, vs1_line_start);
+       nxptda_write2(sc, TDA_VS_PIX_STRT_1_MSB, vs1_pix_start);
+       nxptda_write2(sc, TDA_VS_LINE_END_1_MSB, vs1_line_end);
+       nxptda_write2(sc, TDA_VS_PIX_END_1_MSB, vs1_pix_stop);
+       nxptda_write2(sc, TDA_VS_LINE_STRT_2_MSB, vs2_line_start);
+       nxptda_write2(sc, TDA_VS_PIX_STRT_2_MSB, vs2_pix_start);
+       nxptda_write2(sc, TDA_VS_LINE_END_2_MSB, vs2_line_end);
+       nxptda_write2(sc, TDA_VS_PIX_END_2_MSB, vs2_pix_stop);
+       nxptda_write2(sc, TDA_HS_PIX_START_MSB, hs_pix_start);
+       nxptda_write2(sc, TDA_HS_PIX_STOP_MSB, hs_pix_stop);
+       nxptda_write2(sc, TDA_VWIN_START_1_MSB, vwin1_line_start);
+       nxptda_write2(sc, TDA_VWIN_END_1_MSB, vwin1_line_end);
+       nxptda_write2(sc, TDA_VWIN_START_2_MSB, vwin2_line_start);
+       nxptda_write2(sc, TDA_VWIN_END_2_MSB, vwin2_line_end);
+       nxptda_write2(sc, TDA_DE_START_MSB, de_start);
+       nxptda_write2(sc, TDA_DE_STOP_MSB, de_stop);
+
+       nxptda_write(sc, TDA_ENABLE_SPACE, 0x00);
+
+       /*  must be last register set */
+       nxptda_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
+
+       return ret;
+}
+
+int
+nxptda_get_edid(uint8_t *buf, int buflen)
+{
+       int ret = 0, i;
+       struct nxptda_softc *sc = nxptda_cd.cd_devs[0];
+
+       if (buflen < EDID_LENGTH || sc->sc_edid == NULL)
+           return -1;
+
+       for (i = 0; i < EDID_LENGTH; i++)
+           buf[i] = sc->sc_edid[i];
+
+       return ret;
+}
+
+int
+nxptda_set_videomode(struct videomode *mode)
+{
+       int ret = 0;
+       struct nxptda_softc *sc = nxptda_cd.cd_devs[0];
+
+       ret = nxptda_init_encoder(sc, mode);
+
+       return ret;
+}
Index: sys/arch/armv7/omap/nxptdavar.h
===================================================================
RCS file: sys/arch/armv7/omap/nxptdavar.h
diff -N sys/arch/armv7/omap/nxptdavar.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/armv7/omap/nxptdavar.h     3 Apr 2017 05:24:34 -0000
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <i...@ce.gl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int    nxptda_get_edid(uint8_t *, int);
+int    nxptda_set_videomode(struct videomode *);
Index: sys/arch/armv7/omap/prcm.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/omap/prcm.c,v
retrieving revision 1.11
diff -u -p -p -u -r1.11 prcm.c
--- sys/arch/armv7/omap/prcm.c  18 Jul 2016 15:03:01 -0000      1.11
+++ sys/arch/armv7/omap/prcm.c  3 Apr 2017 05:24:34 -0000
@@ -75,7 +75,9 @@ uint32_t prcm_fmask_mask[PRCM_REG_MAX];
 uint32_t prcm_imask_addr[PRCM_REG_MAX];
 uint32_t prcm_fmask_addr[PRCM_REG_MAX];
 
-#define SYS_CLK                13 /* SYS_CLK speed in MHz */
+#define SYS_CLK                        13    /* SYS_CLK speed in MHz */
+#define PRCM_AM335X_MASTER_OSC 24000 /* KHz */
+
 
 struct prcm_softc {
        struct device           sc_dev;
@@ -97,6 +99,7 @@ int   prcm_setup_dpll5(struct prcm_softc *
 uint32_t prcm_v3_bit(int mod);
 uint32_t prcm_am335x_clkctrl(int mod);
 
+void prcm_am335x_setup(struct prcm_softc *);
 void prcm_am335x_enablemodule(struct prcm_softc *, int);
 void prcm_am335x_setclock(struct prcm_softc *, int, int);
 
@@ -132,7 +135,7 @@ prcm_attach(struct device *parent, struc
                    sc->sc_dev.dv_xname);
 
        if (fdt_is_compatible(node, "ti,am33xx")) {
-               sc->sc_setup = NULL;
+               sc->sc_setup = prcm_am335x_setup; 
                sc->sc_enablemodule = prcm_am335x_enablemodule;
                sc->sc_setclock = prcm_am335x_setclock;
        } else if (fdt_is_compatible(node, "ti,omap3")) {
@@ -336,6 +339,8 @@ prcm_am335x_clkctrl(int mod)
                return PRCM_AM335X_I2C1_CLKCTRL;
        case PRCM_I2C2:
                return PRCM_AM335X_I2C2_CLKCTRL;
+       case PRCM_LCDC:
+               return PRCM_AM335X_LCDC_CLKCTRL;
        default:
                panic("%s: module not found\n", __func__);
        }
@@ -350,6 +355,37 @@ prcm_enablemodule(int mod)
                panic("%s: not initialised!", __func__);
 
        sc->sc_enablemodule(sc, mod);
+}
+
+void
+prcm_am335x_setup(struct prcm_softc *sc)
+{
+       uint32_t oreg;
+
+       /* Set display PLL for standard VGA resolution (640x480) */
+       oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, 
PRCM_AM335X_DISP_CLKMODE);
+       oreg &= ~0x7;
+       oreg |= 0x4;
+       bus_space_write_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_CLKMODE, 
oreg);
+       while(!(bus_space_read_4(sc->sc_iot, sc->sc_prcm, 
PRCM_AM335X_DISP_IDLEST
+           & 0x10)));
+
+       oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, 
PRCM_AM335X_DISP_CLKSEL);
+       oreg &= 0xFFF80000;
+       oreg |= 0x2F;
+       oreg |= (0x3EF << 8);
+       bus_space_write_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_CLKSEL, 
oreg);
+
+       oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_M2);
+       oreg &= ~(0x1F);
+       oreg |= 0xA;
+       bus_space_write_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_M2, oreg);
+
+       oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, 
PRCM_AM335X_DISP_CLKMODE);
+       oreg |= 0x7;
+       bus_space_write_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_CLKMODE, 
oreg);
+       while(!(bus_space_read_4(sc->sc_iot, sc->sc_prcm, 
PRCM_AM335X_DISP_IDLEST
+           & 0x10)));
 }
 
 void
Index: sys/arch/armv7/omap/prcmvar.h
===================================================================
RCS file: /cvs/src/sys/arch/armv7/omap/prcmvar.h,v
retrieving revision 1.6
diff -u -p -p -u -r1.6 prcmvar.h
--- sys/arch/armv7/omap/prcmvar.h       18 Jul 2016 15:03:01 -0000      1.6
+++ sys/arch/armv7/omap/prcmvar.h       3 Apr 2017 05:24:34 -0000
@@ -54,6 +54,7 @@ enum PRCM_MODULES {
        PRCM_I2C1,
        PRCM_I2C2,
        PRCM_I2C3,
+       PRCM_LCDC,
 };
 
 #define PRCM_REG_MAX   6
Index: share/man/man4/man4.armv7/amdisplay.4
===================================================================
RCS file: share/man/man4/man4.armv7/amdisplay.4
diff -N share/man/man4/man4.armv7/amdisplay.4
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ share/man/man4/man4.armv7/amdisplay.4       3 Apr 2017 05:24:42 -0000
@@ -0,0 +1,73 @@
+.\" Copyright (c) 2017 Ian Sutton <i...@ce.gl>$
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: April 2 2017 $
+.Dt AMDISPLAY 4 armv7
+.Os
+.Sh NAME
+.Nm amdisplay ,
+.Nm nxptda
+.Nd Texas Instruments am335x LCD display driver
+.Sh SYNOPSIS
+.Nm "amdisplay* at simplebus0"
+.Nm "nxptda* at iic*"
+.Nm "wsdisplay* at amdisplay*"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports the LCD controller integrated in Texas Instruments' line of
+AM335x SoCs.
+The LCDC reads a framebuffer from memory via DMA and scans it out
+at the proper frequency to suit a display (along with the nessecary
+hsync/vsync/etc signals) to a PHY transmitter.
+The BeagleBone Black uses
+NXP/Freescale's TDA19988 HDMI transmitter which is additionally supported by 
the
+.Nm nxptda
+driver.
+.Sh SEE ALSO
+.Xr intro 4 ,
+.Xr wsdisplay 4 ,
+.Xr wsfb 4
+.Sh HISTORY
+The
+.Nm
+driver does not appear in
+.Ox 6.1
+currently.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Ian Sutton Aq Mt i...@ce.gl .
+The
+.Nm nxptda
+driver was written by
+.An Oleksandr Tymoshenko Aq Mt go...@freebsd.org
+and later ported to OpenBSD.
+.Sh CAVEATS
+On the BeagleBone Black, the LCDC and onboard eMMC NAND chip share the same set
+of pads such that only one can be wired and used at a time.
+If you wish to boot from or use the onboard storage, you must disable
+.Nm
+via
+.Xr config 8
+in your kernel.
+.Pp
+Display must be attached at boot time, otherwise
+.Nm
+will fail to configure.
+Hotplugging is not supported.
+.Sh BUGS
+Only VGA mode (640x480) at 16 bit color depth is currently supported.

Reply via email to