summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorians <ians@cvs.openbsd.org>2017-08-14 21:46:03 +0000
committerians <ians@cvs.openbsd.org>2017-08-14 21:46:03 +0000
commit1e37fb8ebde4489a5008d62f99b2114a87bf922e (patch)
tree618178aa4ac969af876b83f97fa8033feac32990 /sys
parentc3efd549bd5b2a587f2e84163f9f7b20146b7c9b (diff)
Add the amdisplay(4) and nxphdmi(4) drivers.
ok kettenis@ patrick@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/armv7/conf/GENERIC5
-rw-r--r--sys/arch/armv7/omap/am335x_prcmreg.h7
-rw-r--r--sys/arch/armv7/omap/amdisplay.c643
-rw-r--r--sys/arch/armv7/omap/amdisplayreg.h138
-rw-r--r--sys/arch/armv7/omap/files.omap12
-rw-r--r--sys/arch/armv7/omap/nxphdmi.c790
-rw-r--r--sys/arch/armv7/omap/nxphdmivar.h18
-rw-r--r--sys/arch/armv7/omap/prcm.c49
-rw-r--r--sys/arch/armv7/omap/prcmvar.h3
9 files changed, 1659 insertions, 6 deletions
diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC
index 27d558ee0b4..3df5045e810 100644
--- a/sys/arch/armv7/conf/GENERIC
+++ b/sys/arch/armv7/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.87 2017/07/25 20:39:12 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.88 2017/08/14 21:46:02 ians Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -81,6 +81,9 @@ sdmmc* at ommmc? # SD/MMC bus
omehci* at fdt? # EHCI
usb* at omehci?
+nxphdmi* at iic? # TDA19988 HDMI PHY
+amdisplay* at fdt? # AM335x LCD controller
+wsdisplay* at amdisplay?
# Sunxi A1x/A20 SoC
sxiintc* at fdt? # A1x interrupt controller
diff --git a/sys/arch/armv7/omap/am335x_prcmreg.h b/sys/arch/armv7/omap/am335x_prcmreg.h
index 66f3fa7323f..bb5de82c69f 100644
--- a/sys/arch/armv7/omap/am335x_prcmreg.h
+++ b/sys/arch/armv7/omap/am335x_prcmreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: am335x_prcmreg.h,v 1.4 2014/03/18 07:34:17 syl Exp $ */
+/* $OpenBSD: am335x_prcmreg.h,v 1.5 2017/08/14 21:46:02 ians Exp $ */
/*
* Copyright (c) 2013 Raphael Graf <r@undefined.ch>
*
@@ -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
diff --git a/sys/arch/armv7/omap/amdisplay.c b/sys/arch/armv7/omap/amdisplay.c
new file mode 100644
index 00000000000..9b12227ee6c
--- /dev/null
+++ b/sys/arch/armv7/omap/amdisplay.c
@@ -0,0 +1,643 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <ian@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/nxphdmivar.h>
+
+#ifdef LCD_DEBUG
+#define str(X) #X
+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 DEVNAME(_s) ((_s)->sc_dev.dv_xname)
+
+#define LCD_MAX_PELCLK 170000 /* KHz */
+#define LCD_MASTER_OSC 24000000 /* Hz */
+#define LCD_M1_MAX 2048
+#define LCD_M2_MAX 31
+#define LCD_N_MAX 128
+
+#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 sc_wsd;
+ struct wsscreen_list sc_wsl;
+ struct wsscreen_descr *sc_scrlist[1];
+};
+
+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 *);
+paddr_t amdisplay_mmap(void *, off_t, int);
+int amdisplay_alloc_screen(void *, const struct wsscreen_descr *,
+ void **, int *, int *, long *);
+
+int amdisplay_setup_dma(struct amdisplay_softc *);
+void amdisplay_conf_crt_timings(struct amdisplay_softc *);
+void amdisplay_calc_freq(uint64_t);
+
+struct wsdisplay_accessops amdisplay_accessops = {
+ .ioctl = amdisplay_ioctl,
+ .mmap = amdisplay_mmap,
+ .alloc_screen = amdisplay_alloc_screen,
+ .free_screen = rasops_free_screen,
+ .show_screen = rasops_show_screen,
+ .getchar = rasops_getchar,
+ .load_font = rasops_load_font,
+ .list_font = rasops_list_font,
+};
+
+struct cfattach amdisplay_ca = {
+ sizeof(struct amdisplay_softc), amdisplay_match, amdisplay_attach,
+ amdisplay_detach
+};
+
+struct cfdriver amdisplay_cd = {
+ NULL, "amdisplay", DV_DULL
+};
+
+#ifdef LCD_DEBUG
+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 lockout 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);
+}
+#endif
+
+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;
+ uint64_t pel_clk;
+ 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 (nxphdmi_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;
+
+#ifdef LCD_DEBUG
+ edid_print(&edid);
+#endif
+
+ /* determine max supported resolution our clock signal can handle */
+ for (; i < edid.edid_nmodes - 1; i++) {
+ if (edid.edid_modes[i].dot_clock < LCD_MAX_PELCLK
+ && edid.edid_modes[i].dot_clock > pel_clk) {
+ pel_clk = edid.edid_modes[i].dot_clock;
+ memcpy(sc->sc_active_mode, &edid.edid_modes[i],
+ sizeof(struct videomode));
+ }
+ }
+
+ i = 0;
+ printf("%s: %s :: %d KHz pclk\n", DEVNAME(sc),
+ sc->sc_active_mode->name, sc->sc_active_mode->dot_clock);
+
+ pel_clk *= 2000;
+ amdisplay_calc_freq(pel_clk);
+
+ 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 / 8;
+
+ 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_rpos = 0;
+ sc->sc_ro.ri_rnum = 5;
+ sc->sc_ro.ri_gpos = 5;
+ sc->sc_ro.ri_gnum = 6;
+ sc->sc_ro.ri_bpos = 11;
+ sc->sc_ro.ri_bnum = 5;
+ sc->sc_ro.ri_hw = sc;
+ sc->sc_ro.ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY |
+ RI_CLEAR | RI_FULLCLEAR;
+
+ if (rasops_init(&sc->sc_ro, 200, 200)) {
+ 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 */
+ nxphdmi_set_videomode(sc->sc_active_mode);
+
+ /* latch pins/pads according to fdt node */
+ pinctrl_byphandle(LCD_FDT_PHANDLE);
+
+ /* 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)
+ | LCD_LCDDMA_CTRL_FRAME_MODE;
+ 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));
+ HWRITE4(sc, LCD_LCDDMA_FB1, sc->sc_fb0_dma_segs[0].ds_addr);
+ HWRITE4(sc, LCD_LCDDMA_FB1_CEIL, (sc->sc_fb0_dma_segs[0].ds_addr
+ + sc->sc_fb_size));
+
+ /* 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);
+
+ /* configure wsdisplay descr. */
+ strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name));
+ sc->sc_wsd.capabilities = sc->sc_ro.ri_caps;
+ sc->sc_wsd.nrows = sc->sc_ro.ri_rows;
+ sc->sc_wsd.ncols = sc->sc_ro.ri_cols;
+ sc->sc_wsd.textops = &sc->sc_ro.ri_ops;
+ sc->sc_wsd.fontwidth = sc->sc_ro.ri_font->fontwidth;
+ sc->sc_wsd.fontheight = sc->sc_ro.ri_font->fontheight;
+
+ sc->sc_scrlist[0] = &sc->sc_wsd;
+ sc->sc_wsl.nscreens = 1;
+ sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
+
+ /* attach console */
+ memset(&wsaa, 0, sizeof(wsaa));
+ wsaa.scrdata = &sc->sc_wsl;
+ wsaa.accessops = &amdisplay_accessops;
+ wsaa.accesscookie = &sc->sc_ro;
+
+ config_found_sm(self, &wsaa, wsemuldisplaydevprint,
+ wsemuldisplaydevsubmatch);
+
+ /* enable controller */
+ HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN);
+}
+
+int
+amdisplay_detach(struct device *self, int flags)
+{
+ struct amdisplay_softc *sc = (struct amdisplay_softc *)self;
+
+ 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;
+
+ reg = HREAD4(sc, LCD_IRQSTATUS);
+ HWRITE4(sc, LCD_IRQSTATUS, reg);
+
+ 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);
+ HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_PALMODE);
+ HSET4(sc, LCD_RASTER_CTRL, 0x02 << 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)) {
+ 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);
+ 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);
+
+ 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);
+}
+
+void
+amdisplay_calc_freq(uint64_t freq)
+{
+ uint64_t mul, div, i, j, delta, min_delta;
+
+ min_delta = freq;
+ for (i = 1; i < LCD_M1_MAX; i++) {
+ for (j = 1; j < LCD_N_MAX; j++) {
+ delta = abs(freq - i * (LCD_MASTER_OSC / j));
+ if (delta < min_delta) {
+ mul = i;
+ div = j;
+ min_delta = delta;
+ }
+ if (min_delta == 0)
+ break;
+ }
+ }
+ div--;
+
+ prcm_setclock(4, div);
+ prcm_setclock(3, mul);
+ prcm_setclock(5, 1);
+}
+
+int
+amdisplay_ioctl(void *sconf, u_long cmd, caddr_t data, int flat, struct proc *p)
+{
+ struct rasops_info *ri = sconf;
+ struct wsdisplay_fbinfo *wdf;
+
+ switch (cmd) {
+ case WSDISPLAYIO_GTYPE:
+ *(int *)data = WSDISPLAY_TYPE_UNKNOWN;
+ return 0;
+ case WSDISPLAYIO_GINFO:
+ wdf = (struct wsdisplay_fbinfo *)data;
+ wdf->width = ri->ri_width;
+ wdf->height = ri->ri_height;
+ wdf->depth = ri->ri_depth;
+ wdf->cmsize = 0;
+ break;
+ case WSDISPLAYIO_LINEBYTES:
+ *(u_int *)data = ri->ri_stride;
+ break;
+ case WSDISPLAYIO_SMODE:
+ break;
+ case WSDISPLAYIO_GETSUPPORTEDDEPTH:
+ switch (ri->ri_depth) {
+ case 32:
+ *(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
+ break;
+ case 24:
+ *(u_int *)data = WSDISPLAYIO_DEPTH_24_24;
+ break;
+ case 16:
+ *(u_int *)data = WSDISPLAYIO_DEPTH_16;
+ break;
+ case 15:
+ *(u_int *)data = WSDISPLAYIO_DEPTH_15;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+
+ }
+
+ return 0;
+}
+
+paddr_t
+amdisplay_mmap(void *sconf, off_t off, int prot)
+{
+ struct rasops_info *ri = sconf;
+ struct amdisplay_softc *sc = ri->ri_hw;
+
+ if (off < 0 || off >= sc->sc_fb_size)
+ return -1;
+
+ return bus_dmamem_mmap(sc->sc_dmat, &sc->sc_fb0_dma_segs[0],
+ sc->sc_fb_dma_nsegs, off, prot, BUS_DMA_COHERENT);
+}
+
+int
+amdisplay_alloc_screen(void *sconf, const struct wsscreen_descr *type,
+ void **cookiep, int *curxp, int *curyp, long *attrp)
+{
+ return rasops_alloc_screen(sconf, cookiep, curxp, curyp, attrp);
+}
+
diff --git a/sys/arch/armv7/omap/amdisplayreg.h b/sys/arch/armv7/omap/amdisplayreg.h
new file mode 100644
index 00000000000..c2013928d4d
--- /dev/null
+++ b/sys/arch/armv7/omap/amdisplayreg.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <ian@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
diff --git a/sys/arch/armv7/omap/files.omap b/sys/arch/armv7/omap/files.omap
index 39512d81eb8..b24d29fe11a 100644
--- a/sys/arch/armv7/omap/files.omap
+++ b/sys/arch/armv7/omap/files.omap
@@ -1,4 +1,4 @@
-# $OpenBSD: files.omap,v 1.20 2017/03/01 04:34:09 jsg Exp $
+# $OpenBSD: files.omap,v 1.21 2017/08/14 21:46:02 ians Exp $
define omap {}
device omap: omap
@@ -86,6 +86,10 @@ device omdisplay: wsemuldisplaydev, rasops16
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 nxphdmi
+attach nxphdmi at i2c
+file arch/armv7/omap/nxphdmi.c nxphdmi
+
diff --git a/sys/arch/armv7/omap/nxphdmi.c b/sys/arch/armv7/omap/nxphdmi.c
new file mode 100644
index 00000000000..ac73d3622b9
--- /dev/null
+++ b/sys/arch/armv7/omap/nxphdmi.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <ian@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 <gonzo@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/nxphdmivar.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 nxphdmi_debug = 1;
+#define DPRINTF(n,s) do { if ((n) <= nxphdmi_debug) printf s; } while (0)
+#else
+#define DPRINTF(n,s) do {} while (0)
+#endif
+
+struct nxphdmi_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 nxphdmi_match(struct device *, void *, void *);
+void nxphdmi_attach(struct device *, struct device *, void *);
+
+int nxphdmi_cec_read(struct nxphdmi_softc *, uint8_t, uint8_t *);
+int nxphdmi_cec_write(struct nxphdmi_softc *, uint8_t, uint8_t);
+int nxphdmi_read(struct nxphdmi_softc *, uint16_t, uint8_t *);
+int nxphdmi_write(struct nxphdmi_softc *, uint16_t, uint8_t);
+int nxphdmi_write2(struct nxphdmi_softc *, uint16_t, uint16_t);
+int nxphdmi_set(struct nxphdmi_softc *, uint16_t, uint8_t);
+int nxphdmi_clear(struct nxphdmi_softc *, uint16_t, uint8_t);
+int nxphdmi_set_page(struct nxphdmi_softc *, uint8_t);
+int nxphdmi_read_edid(struct nxphdmi_softc *);
+int nxphdmi_reset(struct nxphdmi_softc *);
+int nxphdmi_init_encoder(struct nxphdmi_softc *, struct videomode *);
+
+int nxphdmi_get_edid(uint8_t *, int);
+int nxphdmi_set_videomode(struct videomode *);
+
+struct cfattach nxphdmi_ca = {
+ sizeof(struct nxphdmi_softc), nxphdmi_match, nxphdmi_attach
+};
+
+struct cfdriver nxphdmi_cd = {
+ NULL, "nxphdmi", DV_DULL
+};
+
+int
+nxphdmi_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
+nxphdmi_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct nxphdmi_softc *sc = (struct nxphdmi_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 */
+ nxphdmi_cec_write(sc, TDA_CEC_ENAMODS, ENAMODS_RXSENS | ENAMODS_HDMI);
+ delay(1000);
+
+ if (!(nxphdmi_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 */
+ nxphdmi_write(sc, TDA_PLL_SERIAL_1, 0x00);
+ nxphdmi_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1));
+ nxphdmi_write(sc, TDA_PLL_SERIAL_3, 0x00);
+ nxphdmi_write(sc, TDA_SERIALIZER, 0x00);
+ nxphdmi_write(sc, TDA_BUFFER_OUT, 0x00);
+ nxphdmi_write(sc, TDA_PLL_SCG1, 0x00);
+ nxphdmi_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+ nxphdmi_write(sc, TDA_PLL_SCGN1, 0xfa);
+ nxphdmi_write(sc, TDA_PLL_SCGN2, 0x00);
+ nxphdmi_write(sc, TDA_PLL_SCGR1, 0x5b);
+ nxphdmi_write(sc, TDA_PLL_SCGR2, 0x00);
+ nxphdmi_write(sc, TDA_PLL_SCG2, 0x10);
+
+ /* Write the default value MUX register */
+ nxphdmi_write(sc, TDA_MUX_VP_VIP_OUT, 0x24);
+
+ res |= nxphdmi_read(sc, TDA_VERSION, &data);
+ version |= data;
+ res |= nxphdmi_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;
+ }
+
+ nxphdmi_write(sc, TDA_DDC_CTRL, DDC_ENABLE);
+ nxphdmi_write(sc, TDA_TX3, 39);
+
+ nxphdmi_cec_write(sc, TDA_CEC_FRO_IM_CLK_CTRL,
+ CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
+
+ if (nxphdmi_read_edid(sc)) {
+ DPRINTF(3,("%s: failed to read EDID bits, exiting!\n",
+ DEVNAME(sc)));
+ return;
+ }
+
+ /* Default values for RGB 4:4:4 mapping */
+ nxphdmi_write(sc, TDA_VIP_CNTRL_0, 0x23);
+ nxphdmi_write(sc, TDA_VIP_CNTRL_1, 0x01);
+ nxphdmi_write(sc, TDA_VIP_CNTRL_2, 0x45);
+
+ iic_release_bus(sc->sc_tag, 0);
+}
+
+int
+nxphdmi_cec_read(struct nxphdmi_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
+nxphdmi_cec_write(struct nxphdmi_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
+nxphdmi_read(struct nxphdmi_softc *sc, uint16_t reg, uint8_t *buf)
+{
+ int ret = 0;
+
+ nxphdmi_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
+nxphdmi_write(struct nxphdmi_softc *sc, uint16_t reg, uint8_t val)
+{
+ int ret = 0;
+ uint8_t sendbuf[] = { REGADDR(reg), val };
+
+ nxphdmi_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
+nxphdmi_write2(struct nxphdmi_softc *sc, uint16_t reg, uint16_t val)
+{
+ int ret = 0;
+ uint8_t sendbuf[] = { REGADDR(reg), val >> 8, val & 0xff };
+
+ nxphdmi_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
+nxphdmi_set(struct nxphdmi_softc *sc, uint16_t reg, uint8_t bits)
+{
+ int ret = 0;
+ uint8_t buf;
+
+ ret |= nxphdmi_read(sc, reg, &buf);
+ buf |= bits;
+ ret |= nxphdmi_write(sc, reg, buf);
+
+ return ret;
+}
+
+int
+nxphdmi_clear(struct nxphdmi_softc *sc, uint16_t reg, uint8_t bits)
+{
+ int ret = 0;
+ uint8_t buf;
+
+ ret |= nxphdmi_read(sc, reg, &buf);
+ buf &= ~bits;
+ ret |= nxphdmi_write(sc, reg, buf);
+
+ return ret;
+}
+
+int
+nxphdmi_set_page(struct nxphdmi_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
+nxphdmi_read_edid(struct nxphdmi_softc *sc)
+{
+ int i = 0, ret = 0;
+ uint8_t reg;
+
+ nxphdmi_set(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+
+ /* Block 0 */
+ nxphdmi_write(sc, TDA_DDC_ADDR, 0xa0);
+ nxphdmi_write(sc, TDA_DDC_OFFS, 0x00);
+ nxphdmi_write(sc, TDA_DDC_SEGM_ADDR, 0x60);
+ nxphdmi_write(sc, TDA_DDC_SEGM, 0x00);
+
+ nxphdmi_write(sc, TDA_EDID_CTRL, 1);
+ nxphdmi_write(sc, TDA_EDID_CTRL, 0);
+
+ for (; i < MAX_READ_ATTEMPTS; i++) {
+ nxphdmi_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;
+ }
+
+ nxphdmi_set_page(sc, 0x09);
+
+ reg = 0x00;
+ DPRINTF(1,("%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(1,("\n%s: ", DEVNAME(sc)));
+ DPRINTF(1,("%02x", sc->sc_edid[i]));
+ reg++;
+ }
+ DPRINTF(1,("\n%s: --------------------------------\n", DEVNAME(sc)));
+
+ nxphdmi_clear(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+ nxphdmi_set(sc, TDA_TX4, TX4_PD_RAM);
+
+ return ret;
+}
+
+int
+nxphdmi_reset(struct nxphdmi_softc *sc)
+{
+ int ret = 0;
+
+ /* reset core */
+ ret |= nxphdmi_set(sc, TDA_SOFTRESET, 3);
+ delay(100);
+ ret |= nxphdmi_clear(sc, TDA_SOFTRESET, 3);
+ delay(100);
+
+ /* reset transmitter */
+ ret |= nxphdmi_set(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+ ret |= nxphdmi_clear(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+
+ return ret;
+}
+
+int
+nxphdmi_init_encoder(struct nxphdmi_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 */
+ nxphdmi_set(sc, TDA_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
+ nxphdmi_clear(sc, TDA_HDCP_TX33, HDCP_TX33_HDMI);
+ nxphdmi_write(sc, TDA_ENC_CNTRL, ENC_CNTRL_DVI_MODE);
+
+ /* no pre-filter or interpolator */
+ nxphdmi_write(sc, TDA_HVF_CNTRL_0,
+ HVF_CNTRL_0_INTPOL_BYPASS | HVF_CNTRL_0_PREFIL_NONE);
+ nxphdmi_write(sc, TDA_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
+ nxphdmi_write(sc, TDA_VIP_CNTRL_4,
+ VIP_CNTRL_4_BLANKIT_NDE | VIP_CNTRL_4_BLC_NONE);
+
+ nxphdmi_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR);
+ nxphdmi_clear(sc, TDA_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IP);
+ nxphdmi_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE);
+ nxphdmi_write(sc, TDA_SERIALIZER, 0);
+ nxphdmi_write(sc, TDA_HVF_CNTRL_1, HVF_CNTRL_1_VQR_FULL);
+
+ nxphdmi_write(sc, TDA_RPT_CNTRL, 0);
+ nxphdmi_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
+ SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+
+ nxphdmi_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
+ PLL_SERIAL_2_SRL_PR(0));
+
+ nxphdmi_set(sc, TDA_MAT_CONTRL, MAT_CONTRL_MAT_BP);
+
+ nxphdmi_write(sc, TDA_ANA_GENERAL, 0x09);
+
+ nxphdmi_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;
+ nxphdmi_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;
+ nxphdmi_write(sc, TDA_TBG_CNTRL_1, reg);
+
+ /* Program timing */
+ nxphdmi_write(sc, TDA_VIDFORMAT, 0x00);
+
+ nxphdmi_write2(sc, TDA_REFPIX_MSB, ref_pix);
+ nxphdmi_write2(sc, TDA_REFLINE_MSB, ref_line);
+ nxphdmi_write2(sc, TDA_NPIX_MSB, n_pix);
+ nxphdmi_write2(sc, TDA_NLINE_MSB, n_line);
+
+ nxphdmi_write2(sc, TDA_VS_LINE_STRT_1_MSB, vs1_line_start);
+ nxphdmi_write2(sc, TDA_VS_PIX_STRT_1_MSB, vs1_pix_start);
+ nxphdmi_write2(sc, TDA_VS_LINE_END_1_MSB, vs1_line_end);
+ nxphdmi_write2(sc, TDA_VS_PIX_END_1_MSB, vs1_pix_stop);
+ nxphdmi_write2(sc, TDA_VS_LINE_STRT_2_MSB, vs2_line_start);
+ nxphdmi_write2(sc, TDA_VS_PIX_STRT_2_MSB, vs2_pix_start);
+ nxphdmi_write2(sc, TDA_VS_LINE_END_2_MSB, vs2_line_end);
+ nxphdmi_write2(sc, TDA_VS_PIX_END_2_MSB, vs2_pix_stop);
+ nxphdmi_write2(sc, TDA_HS_PIX_START_MSB, hs_pix_start);
+ nxphdmi_write2(sc, TDA_HS_PIX_STOP_MSB, hs_pix_stop);
+ nxphdmi_write2(sc, TDA_VWIN_START_1_MSB, vwin1_line_start);
+ nxphdmi_write2(sc, TDA_VWIN_END_1_MSB, vwin1_line_end);
+ nxphdmi_write2(sc, TDA_VWIN_START_2_MSB, vwin2_line_start);
+ nxphdmi_write2(sc, TDA_VWIN_END_2_MSB, vwin2_line_end);
+ nxphdmi_write2(sc, TDA_DE_START_MSB, de_start);
+ nxphdmi_write2(sc, TDA_DE_STOP_MSB, de_stop);
+
+ nxphdmi_write(sc, TDA_ENABLE_SPACE, 0x00);
+
+ /* must be last register set */
+ nxphdmi_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
+
+ return ret;
+}
+
+int
+nxphdmi_get_edid(uint8_t *buf, int buflen)
+{
+ int ret = 0, i;
+ struct nxphdmi_softc *sc = nxphdmi_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
+nxphdmi_set_videomode(struct videomode *mode)
+{
+ int ret = 0;
+ struct nxphdmi_softc *sc = nxphdmi_cd.cd_devs[0];
+
+ ret = nxphdmi_init_encoder(sc, mode);
+
+ return ret;
+}
diff --git a/sys/arch/armv7/omap/nxphdmivar.h b/sys/arch/armv7/omap/nxphdmivar.h
new file mode 100644
index 00000000000..9a05f6839b9
--- /dev/null
+++ b/sys/arch/armv7/omap/nxphdmivar.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <ian@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 nxphdmi_get_edid(uint8_t *, int);
+int nxphdmi_set_videomode(struct videomode *);
diff --git a/sys/arch/armv7/omap/prcm.c b/sys/arch/armv7/omap/prcm.c
index a82eb67f510..1f2ab7fe5b6 100644
--- a/sys/arch/armv7/omap/prcm.c
+++ b/sys/arch/armv7/omap/prcm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: prcm.c,v 1.11 2016/07/18 15:03:01 jsg Exp $ */
+/* $OpenBSD: prcm.c,v 1.12 2017/08/14 21:46:02 ians Exp $ */
/*
* Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
*
@@ -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;
@@ -240,6 +242,47 @@ prcm_am335x_setclock(struct prcm_softc *sc, int clock, int speed)
reg |=0x02;
bus_space_write_4(sc->sc_iot, sc->sc_prcm,
PRCM_AM335X_CLKSEL_TIMER3_CLK, reg);
+ } else if (clock == 3) { /* DISP M1 */
+ 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 &= 0xFFF800FF;
+ oreg |= (speed & 0x7FF) << 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_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)));
+ } else if (clock == 4) { /* DISP N */
+ 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 &= 0xFFFFFF80;
+ oreg |= speed & 0x7F;
+ 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_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)));
+ } else if (clock == 5) { /* DISP M2 */
+ oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_M2);
+ oreg &= ~(0x1F);
+ oreg |= speed & 0x1F;
+ bus_space_write_4(sc->sc_iot, sc->sc_prcm, PRCM_AM335X_DISP_M2, oreg);
}
}
@@ -336,6 +379,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__);
}
diff --git a/sys/arch/armv7/omap/prcmvar.h b/sys/arch/armv7/omap/prcmvar.h
index 18a18a88918..fe715b6adba 100644
--- a/sys/arch/armv7/omap/prcmvar.h
+++ b/sys/arch/armv7/omap/prcmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: prcmvar.h,v 1.6 2016/07/18 15:03:01 jsg Exp $ */
+/* $OpenBSD: prcmvar.h,v 1.7 2017/08/14 21:46:02 ians Exp $ */
/*
* Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
*
@@ -54,6 +54,7 @@ enum PRCM_MODULES {
PRCM_I2C1,
PRCM_I2C2,
PRCM_I2C3,
+ PRCM_LCDC,
};
#define PRCM_REG_MAX 6