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