diff options
author | ians <ians@cvs.openbsd.org> | 2017-08-14 21:46:03 +0000 |
---|---|---|
committer | ians <ians@cvs.openbsd.org> | 2017-08-14 21:46:03 +0000 |
commit | 1e37fb8ebde4489a5008d62f99b2114a87bf922e (patch) | |
tree | 618178aa4ac969af876b83f97fa8033feac32990 /sys | |
parent | c3efd549bd5b2a587f2e84163f9f7b20146b7c9b (diff) |
Add the amdisplay(4) and nxphdmi(4) drivers.
ok kettenis@ patrick@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/armv7/conf/GENERIC | 5 | ||||
-rw-r--r-- | sys/arch/armv7/omap/am335x_prcmreg.h | 7 | ||||
-rw-r--r-- | sys/arch/armv7/omap/amdisplay.c | 643 | ||||
-rw-r--r-- | sys/arch/armv7/omap/amdisplayreg.h | 138 | ||||
-rw-r--r-- | sys/arch/armv7/omap/files.omap | 12 | ||||
-rw-r--r-- | sys/arch/armv7/omap/nxphdmi.c | 790 | ||||
-rw-r--r-- | sys/arch/armv7/omap/nxphdmivar.h | 18 | ||||
-rw-r--r-- | sys/arch/armv7/omap/prcm.c | 49 | ||||
-rw-r--r-- | sys/arch/armv7/omap/prcmvar.h | 3 |
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, ®); + 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, ®, 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 |