> Date: Sat, 6 Aug 2016 04:26:27 -0400 > From: Ian Sutton <[email protected]>
Hi Ian, > 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). Right. I had a quick look at the documentation there and quickly decided it wasn't an immediate priority. Note that on many platforms (including i.MX6) u-boot already sets up a framebuffer. So if we'd know framebuffer address and the layout of the framebuffer it would be trivial to attach a rasops-based wsdisplay(4) to that. There is a "simple-framebuffer" device tree binding for passing that information; see Documentation/devicetree/bindings/display/simple-framebuffer.txt in the Linux tree. Unofrtunately u-boot only adds the appropriate nodes to the tree on the Allwinner and Raspberry Pi platforms. I don't think it would be too difficult to add it for other platforms though. Of course this wouldn't allow you to change resolution and color depth. But it would be enough to run X with the wsfb driver. > 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. If it interests you, go for it. But I think that for most of our users a simple framebuffer as I sketched above would be good enough. So for me, this doesn't have a very high priority. > 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 > >
