amdisplay(4) drives the LCD controller in am335x SoCs (beaglebone black
for us). Patch is unfinished but is further along than imxdisplay(4).
(imx6q's display subsystem is enormously complicated and poorly
documented; I intend to finish both but am working on this one for now).

Interestingly, the am335x SoC does not have a HDMI/DP/etc transmitter on
silicon -- the BBB has its LCDC pins wired to a TDA19988 HDMI
transmitter which is additionally backwired to one of the am335x's i2c
ports:

"nxp,tda998x" at iic0 addr 0x70 not configured

Without a TDA19988 driver we are unable to probe for an attached
display's EDID bits thus unable to tailor timing/videomode parameters
on a per-display basis.

I am working on such a driver but would like to ask developers if it
would be worth it, or if there is more important armv7 work to be done
elsewhere.

Ian

Index: sys/arch/armv7/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/armv7/conf/GENERIC,v
retrieving revision 1.31
diff -u -p -r1.31 GENERIC
--- sys/arch/armv7/conf/GENERIC 12 Jul 2016 19:17:49 -0000      1.31
+++ sys/arch/armv7/conf/GENERIC 6 Aug 2016 07:59:48 -0000
@@ -76,6 +76,8 @@ cpsw*         at fdt?
 com*           at fdt?                 # onboard uarts
 ommmc*         at fdt?                 # SD/MMC card controller
 sdmmc*         at ommmc?               # SD/MMC bus
+amdisplay*     at fdt?                 # LCD controller
+wsdisplay*     at amdisplay? console ?
 
 ehci*          at omap?                # EHCI (shim)
 usb*           at ehci?
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 -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        6 Aug 2016 07:59:48 -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
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     6 Aug 2016 07:59:48 -0000
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2016 Ian Sutton <[email protected]>
+ *
+ * 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 <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/ofw/openfirm.h>
+#include <dev/ofw/fdt.h>
+#include <machine/fdt.h>
+
+#include <armv7/omap/prcmvar.h>
+
+/* register offsets */
+#define AMDISPLAY_PID                  0x00
+#define AMDISPLAY_CTRL                 0x04
+#define   AMDISPLAY_CTRL_CLKDIV_MASK   (0xFF << 8)
+#define   AMDISPLAY_CTRL_MODESEL_MASK  (1 << 0)
+#define AMDISPLAY_RASTER_CTRL          0x28
+#define   AMDISPLAY_RASTER_CTRL_TFT24UNPACKED_MASK     (1 << 26)
+#define   AMDISPLAY_RASTER_CTRL_TFT24_MASK             (1 << 25)
+#define   AMDISPLAY_RASTER_CTRL_LCDTFT_MASK            (1 << 7)
+#define   AMDISPLAY_RASTER_CTRL_LCDEN_MASK             (1 << 0)
+#define AMDISPLAY_RASTER_TIMING_0      0x2C
+#define AMDISPLAY_RASTER_TIMING_1      0x30
+#define AMDISPLAY_RASTER_TIMING_2      0x34
+#define AMDISPLAY_RASTER_SUBPANEL      0x38
+#define AMDISPLAY_RASTER_SUBPANEL_2    0x3C
+#define AMDISPLAY_LCDDMA_CTRL          0x40
+#define AMDISPLAY_LCDDMA_FB0_BASE      0x44
+#define AMDISPLAY_LCDDMA_FB0_CEILING   0x48
+#define AMDISPLAY_LCDDMA_FB1_BASE      0x4C
+#define AMDISPLAY_LCDDMA_FB1_CEILING   0x50
+#define AMDISPLAY_SYSCONFIG            0x54
+#define   AMDISPLAY_SYSCONFIG_STANDBYMODE_MASK         (3 << 4)
+#define   AMDISPLAY_SYSCONFIG_IDLEMODE_MASK            (3 << 2)
+#define   AMDISPLAY_SYSCONFIG_STANDBYMODE_SHAMT                4
+#define   AMDISPLAY_SYSCONFIG_IDLEMODE_SHAMT           2
+#define AMDISPLAY_IRQSTATUS_RAW                0x58
+#define AMDISPLAY_IRQSTATUS            0x5C
+#define AMDISPLAY_IRQENABLE_SET                0x60
+#define AMDISPLAY_IRQENABLE_CLEAR      0x64
+#define AMDISPLAY_CLKC_ENABLE          0x6C
+#define AMDISPLAY_CLKC_RESET           0x70
+
+/* intr. masks */
+#define AMDISPLAY_IRQ_EOF1     (1 << 9)
+#define AMDISPLAY_IRQ_EOF0     (1 << 8)
+#define AMDISPLAY_IRQ_PL       (1 << 6)
+#define AMDISPLAY_IRQ_FUF      (1 << 5)
+#define AMDISPLAY_IRQ_ACB      (1 << 3)
+#define AMDISPLAY_IRQ_SYNC     (1 << 2)
+#define AMDISPLAY_IRQ_RR_DONE  (1 << 1)
+#define AMDISPLAY_IRQ_DONE     (1 << 0)
+
+#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
+
+#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;
+
+       struct amdisplay_panel_data     *sc_active;
+       bus_dmamap_t            sc_fb_dma;
+       bus_dma_segment_t       sc_fb_dma_segs[1];
+       int                     sc_fb_dma_nsegs;
+       size_t                  sc_fb_size;
+       caddr_t                 sc_fb;
+       int                     depth;
+       struct rasops_info      ro;
+};
+
+struct amdisplay_panel_data {
+       int width;
+       int height;
+       int horiz_sync_width;
+       int horiz_front_porch;
+       int horiz_back_porch;
+       int vert_sync_width;
+       int vert_front_porch;
+       int vert_back_porch;
+       int panel_flags;
+       int sync;
+       int depth;
+#define PANEL_SYNC_H_ACTIVE_HIGH 1
+#define PANEL_SYNC_V_ACTIVE_HIGH 2
+       int linebytes;
+};
+
+/* we use this dummy device and hope it applies as the am335x has no on-chip
+ * method of determining peripheral display capabilities. the beaglebone black
+ * has the LCDC pins hardwired to a TDA19988 which sits between LCDC & HDMI
+ * connector and is also wired to an i2c port over which it sends EDID
+ * information needed to properly configure display geometry on a per-panel
+ * basis XXX */
+struct amdisplay_panel_data dummy = {
+       1280,
+       720,
+       44, 88, 148,
+       5, 4, 36,
+       0,
+       0,
+       24,
+       2560
+};
+
+struct wsscreen_descr amdisplay_stdscreen = {
+       "std"
+};
+
+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_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);
+
+void amdisplay_setup_rasops(struct amdisplay_softc *);
+int  amdisplay_setup_dma(struct amdisplay_softc *, struct amdisplay_panel_data 
*);
+
+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
+};
+
+struct cfdriver amdisplay_cd = {
+       NULL, "amdisplay", DV_DULL
+};
+
+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;
+       
+       uint32_t irq, reg;
+
+       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__);
+
+       irq = faa->fa_intr[0];
+
+       prcm_enablemodule(PRCM_LCDC);
+
+       /* force ourselves out of standby/idle states */
+       reg = HREAD4(sc, AMDISPLAY_SYSCONFIG);
+       reg &= ~(AMDISPLAY_SYSCONFIG_STANDBYMODE_MASK | 
AMDISPLAY_SYSCONFIG_IDLEMODE_MASK);
+       reg |= (1 << AMDISPLAY_SYSCONFIG_STANDBYMODE_SHAMT);
+       reg |= (1 << AMDISPLAY_SYSCONFIG_IDLEMODE_SHAMT);
+       HWRITE4(sc, AMDISPLAY_SYSCONFIG, reg);
+
+       sc->sc_ih = arm_intr_establish(irq, IPL_BIO, amdisplay_intr, sc, 
DEVNAME(sc));
+
+       printf("\n");
+
+       /* allocate DMA-able memory for framebuffer */
+       if (amdisplay_setup_dma(sc, &dummy)) {
+           printf("%s: could not create framebuffer!\n", DEVNAME(sc));
+           return;
+       }
+
+       amdisplay_setup_rasops(sc);
+
+       /* set raster mode */
+       HSET4(sc, AMDISPLAY_CTRL, AMDISPLAY_CTRL_MODESEL_MASK);
+
+       /* enable all intrs. */
+       reg = 0;
+       reg |= (AMDISPLAY_IRQ_EOF1 | AMDISPLAY_IRQ_EOF0 | AMDISPLAY_IRQ_PL |
+           AMDISPLAY_IRQ_FUF | AMDISPLAY_IRQ_ACB | AMDISPLAY_IRQ_SYNC |
+           AMDISPLAY_IRQ_RR_DONE | AMDISPLAY_IRQ_DONE);
+       HWRITE4(sc, AMDISPLAY_IRQENABLE_SET, reg);
+
+       wsaa.console = 0;
+       wsaa.scrdata = &amdisplay_screenlist;
+       wsaa.accessops = &amdisplay_accessops;
+       wsaa.accesscookie = sc;
+       wsaa.defaultscreens = 0;
+
+       printf("%s: %dx%d\n", sc->sc_dev.dv_xname, sc->ro.ri_width, 
sc->ro.ri_height);
+
+       //config_found(self, &wsaa, wsemuldisplaydevprint);
+}
+
+void
+amdisplay_setup_rasops(struct amdisplay_softc *sc)
+{
+       sc->ro.ri_depth  = sc->sc_active->depth;
+       sc->ro.ri_width  = sc->sc_active->width;
+       sc->ro.ri_height = sc->sc_active->height;
+       sc->ro.ri_stride = sc->sc_active->width * sc->sc_active->depth;
+       sc->ro.ri_bits   = (void *) sc->sc_fb;
+
+       rasops_init(&(sc->ro), 100, 100);
+}
+
+int
+amdisplay_setup_dma(struct amdisplay_softc *sc, struct amdisplay_panel_data *p)
+{
+       size_t fb_size;
+       int dma_flags     = (cold ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+
+       //fb_size = p->width * p->height * p->depth;
+       fb_size = roundup(p->height * p->linebytes, 16);
+       if(bus_dmamap_create(sc->sc_dmat, fb_size, 1, fb_size, 0, dma_flags,
+           &(sc->sc_fb_dma)))
+               return -1;
+
+       if(bus_dmamem_alloc(sc->sc_dmat, fb_size, 0x100000, 0, 
sc->sc_fb_dma_segs,
+           1, &(sc->sc_fb_dma_nsegs), dma_flags)) /* XXX check alignment */
+               return -1;
+
+       sc->sc_fb = NULL;
+       if(bus_dmamem_map(sc->sc_dmat, sc->sc_fb_dma_segs, sc->sc_fb_dma_nsegs,
+           fb_size, &(sc->sc_fb), dma_flags))
+               return -1;
+
+       sc->sc_active = p;
+       return 0;
+}
+
+int
+amdisplay_intr(void *arg)
+{
+       return 0;
+}
+
+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->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->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->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->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->ro);
+
+       return rasops_list_font(ri, font);
+}
+
+void
+amdisplay_burner(void *sconf, u_int on, u_int flags)
+{
+}
Index: sys/arch/armv7/omap/files.omap
===================================================================
RCS file: /cvs/src/sys/arch/armv7/omap/files.omap,v
retrieving revision 1.13
diff -u -p -r1.13 files.omap
--- sys/arch/armv7/omap/files.omap      26 Jun 2016 09:06:35 -0000      1.13
+++ sys/arch/armv7/omap/files.omap      6 Aug 2016 07:59:48 -0000
@@ -81,6 +81,11 @@ device omdisplay: wsemuldisplaydev, raso
 attach omdisplay at omap
 file arch/armv7/omap/omdisplay.c               omdisplay
 
+# am335x LCD frame buffer
+device amdisplay: wsemuldisplaydev, rasops16
+attach amdisplay at fdt
+file arch/armv7/omap/amdisplay.c               amdisplay
+
 # MCSPI - spi 
 device mcspi 
 attach mcspi at omap
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 -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  6 Aug 2016 07:59:48 -0000
@@ -336,6 +336,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__);
        }
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 -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       6 Aug 2016 07:59:48 -0000
@@ -54,6 +54,7 @@ enum PRCM_MODULES {
        PRCM_I2C1,
        PRCM_I2C2,
        PRCM_I2C3,
+       PRCM_LCDC,
 };
 
 #define PRCM_REG_MAX   6

Reply via email to