diff --git a/sys/arm/allwinner/a10_fb.c b/sys/arm/allwinner/a10_fb.c deleted file mode 100644 index 387d5daa10c1..000000000000 --- a/sys/arm/allwinner/a10_fb.c +++ /dev/null @@ -1,660 +0,0 @@ -/*- - * Copyright (c) 2016 Jared McNeill - * - * 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 ``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 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. - * - * $FreeBSD$ - */ - -/* - * Allwinner A10/A20 Framebuffer - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - -#include -#include - -#include "fb_if.h" -#include "hdmi_if.h" - -#define FB_DEFAULT_W 800 -#define FB_DEFAULT_H 600 -#define FB_DEFAULT_REF 60 -#define FB_BPP 32 -#define FB_ALIGN 0x1000 - -#define HDMI_ENABLE_DELAY 20000 -#define DEBE_FREQ 300000000 - -#define DOT_CLOCK_TO_HZ(c) ((c) * 1000) - -/* Display backend */ -#define DEBE_REG_START 0x800 -#define DEBE_REG_END 0x1000 -#define DEBE_REG_WIDTH 4 -#define DEBE_MODCTL 0x800 -#define MODCTL_ITLMOD_EN (1 << 28) -#define MODCTL_OUT_SEL_MASK (0x7 << 20) -#define MODCTL_OUT_SEL(sel) ((sel) << 20) -#define OUT_SEL_LCD 0 -#define MODCTL_LAY0_EN (1 << 8) -#define MODCTL_START_CTL (1 << 1) -#define MODCTL_EN (1 << 0) -#define DEBE_DISSIZE 0x808 -#define DIS_HEIGHT(h) (((h) - 1) << 16) -#define DIS_WIDTH(w) (((w) - 1) << 0) -#define DEBE_LAYSIZE0 0x810 -#define LAY_HEIGHT(h) (((h) - 1) << 16) -#define LAY_WIDTH(w) (((w) - 1) << 0) -#define DEBE_LAYCOOR0 0x820 -#define LAY_XCOOR(x) ((x) << 16) -#define LAY_YCOOR(y) ((y) << 0) -#define DEBE_LAYLINEWIDTH0 0x840 -#define DEBE_LAYFB_L32ADD0 0x850 -#define LAYFB_L32ADD(pa) ((pa) << 3) -#define DEBE_LAYFB_H4ADD 0x860 -#define LAY0FB_H4ADD(pa) ((pa) >> 29) -#define DEBE_REGBUFFCTL 0x870 -#define REGBUFFCTL_LOAD (1 << 0) -#define DEBE_ATTCTL1 0x8a0 -#define ATTCTL1_FBFMT(fmt) ((fmt) << 8) -#define FBFMT_XRGB8888 9 -#define ATTCTL1_FBPS(ps) ((ps) << 0) -#define FBPS_32BPP_ARGB 0 - -/* Timing controller */ -#define TCON_GCTL 0x000 -#define GCTL_TCON_EN (1 << 31) -#define GCTL_IO_MAP_SEL_TCON1 (1 << 0) -#define TCON_GINT1 0x008 -#define GINT1_TCON1_LINENO(n) (((n) + 2) << 0) -#define TCON0_DCLK 0x044 -#define DCLK_EN 0xf0000000 -#define TCON1_CTL 0x090 -#define TCON1_EN (1 << 31) -#define INTERLACE_EN (1 << 20) -#define TCON1_SRC_SEL(src) ((src) << 0) -#define TCON1_SRC_CH1 0 -#define TCON1_SRC_CH2 1 -#define TCON1_SRC_BLUE 2 -#define TCON1_START_DELAY(sd) ((sd) << 4) -#define TCON1_BASIC0 0x094 -#define TCON1_BASIC1 0x098 -#define TCON1_BASIC2 0x09c -#define TCON1_BASIC3 0x0a0 -#define TCON1_BASIC4 0x0a4 -#define TCON1_BASIC5 0x0a8 -#define BASIC_X(x) (((x) - 1) << 16) -#define BASIC_Y(y) (((y) - 1) << 0) -#define BASIC3_HT(ht) (((ht) - 1) << 16) -#define BASIC3_HBP(hbp) (((hbp) - 1) << 0) -#define BASIC4_VT(vt) ((vt) << 16) -#define BASIC4_VBP(vbp) (((vbp) - 1) << 0) -#define BASIC5_HSPW(hspw) (((hspw) - 1) << 16) -#define BASIC5_VSPW(vspw) (((vspw) - 1) << 0) -#define TCON1_IO_POL 0x0f0 -#define IO_POL_IO2_INV (1 << 26) -#define IO_POL_PHSYNC (1 << 25) -#define IO_POL_PVSYNC (1 << 24) -#define TCON1_IO_TRI 0x0f4 -#define IO0_OUTPUT_TRI_EN (1 << 24) -#define IO1_OUTPUT_TRI_EN (1 << 25) -#define IO_TRI_MASK 0xffffffff -#define START_DELAY(vbl) (MIN(32, (vbl)) - 2) -#define VBLANK_LEN(vt, vd, i) ((((vt) << (i)) >> 1) - (vd) - 2) -#define VTOTAL(vt) ((vt) * 2) -#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) - -struct a10fb_softc { - device_t dev; - device_t fbdev; - struct resource *res[2]; - - /* Framebuffer */ - struct fb_info info; - size_t fbsize; - bus_addr_t paddr; - vm_offset_t vaddr; - - /* HDMI */ - eventhandler_tag hdmi_evh; -}; - -static struct resource_spec a10fb_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* DEBE */ - { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCON */ - { -1, 0 } -}; - -#define DEBE_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) -#define DEBE_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) - -#define TCON_READ(sc, reg) bus_read_4((sc)->res[1], (reg)) -#define TCON_WRITE(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val)) - -static int -a10fb_allocfb(struct a10fb_softc *sc) -{ - sc->vaddr = kmem_alloc_contig(sc->fbsize, M_NOWAIT | M_ZERO, 0, ~0, - FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING); - if (sc->vaddr == 0) { - device_printf(sc->dev, "failed to allocate FB memory\n"); - return (ENOMEM); - } - sc->paddr = pmap_kextract(sc->vaddr); - - return (0); -} - -static void -a10fb_freefb(struct a10fb_softc *sc) -{ - kmem_free(sc->vaddr, sc->fbsize); -} - -static int -a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) -{ - int width, height, interlace, reg; - clk_t clk_ahb, clk_dram, clk_debe; - hwreset_t rst; - uint32_t val; - int error; - - interlace = !!(mode->flags & VID_INTERLACE); - width = mode->hdisplay; - height = mode->vdisplay << interlace; - - /* Leave reset */ - error = hwreset_get_by_ofw_name(sc->dev, 0, "de_be", &rst); - if (error != 0) { - device_printf(sc->dev, "cannot find reset 'de_be'\n"); - return (error); - } - error = hwreset_deassert(rst); - if (error != 0) { - device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n"); - return (error); - } - /* Gating AHB clock for BE */ - error = clk_get_by_ofw_name(sc->dev, 0, "ahb_de_be", &clk_ahb); - if (error != 0) { - device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n"); - return (error); - } - error = clk_enable(clk_ahb); - if (error != 0) { - device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n"); - return (error); - } - /* Enable DRAM clock to BE */ - error = clk_get_by_ofw_name(sc->dev, 0, "dram_de_be", &clk_dram); - if (error != 0) { - device_printf(sc->dev, "cannot find clk 'dram_de_be'\n"); - return (error); - } - error = clk_enable(clk_dram); - if (error != 0) { - device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n"); - return (error); - } - /* Set BE clock to 300MHz and enable */ - error = clk_get_by_ofw_name(sc->dev, 0, "de_be", &clk_debe); - if (error != 0) { - device_printf(sc->dev, "cannot find clk 'de_be'\n"); - return (error); - } - error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN); - if (error != 0) { - device_printf(sc->dev, "cannot set 'de_be' frequency\n"); - return (error); - } - error = clk_enable(clk_debe); - if (error != 0) { - device_printf(sc->dev, "cannot enable clk 'de_be'\n"); - return (error); - } - - /* Initialize all registers to 0 */ - for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH) - DEBE_WRITE(sc, reg, 0); - - /* Enable display backend */ - DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN); - - /* Set display size */ - DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width)); - - /* Set layer 0 size, position, and stride */ - DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width)); - DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0)); - DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP); - - /* Point layer 0 to FB memory */ - DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr)); - DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr)); - - /* Set backend format and pixel sequence */ - DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) | - ATTCTL1_FBPS(FBPS_32BPP_ARGB)); - - /* Enable layer 0, output to LCD, setup interlace */ - val = DEBE_READ(sc, DEBE_MODCTL); - val |= MODCTL_LAY0_EN; - val &= ~MODCTL_OUT_SEL_MASK; - val |= MODCTL_OUT_SEL(OUT_SEL_LCD); - if (interlace) - val |= MODCTL_ITLMOD_EN; - else - val &= ~MODCTL_ITLMOD_EN; - DEBE_WRITE(sc, DEBE_MODCTL, val); - - /* Commit settings */ - DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD); - - /* Start DEBE */ - val = DEBE_READ(sc, DEBE_MODCTL); - val |= MODCTL_START_CTL; - DEBE_WRITE(sc, DEBE_MODCTL, val); - - return (0); -} - -static int -a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq) -{ - clk_t clk_sclk1, clk_sclk2; - int error; - - error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk1", &clk_sclk1); - if (error != 0) { - device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n"); - return (error); - } - error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk2", &clk_sclk2); - if (error != 0) { - device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n"); - return (error); - } - - error = clk_set_freq(clk_sclk2, freq, 0); - if (error != 0) { - device_printf(sc->dev, "cannot set lcd ch1 frequency\n"); - return (error); - } - error = clk_enable(clk_sclk2); - if (error != 0) { - device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n"); - return (error); - } - error = clk_enable(clk_sclk1); - if (error != 0) { - device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n"); - return (error); - } - - return (0); -} - -static int -a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) -{ - u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay; - u_int vtotal, framerate, clk; - clk_t clk_ahb; - hwreset_t rst; - uint32_t val; - int error; - - interlace = !!(mode->flags & VID_INTERLACE); - width = mode->hdisplay; - height = mode->vdisplay; - hspw = mode->hsync_end - mode->hsync_start; - hbp = mode->htotal - mode->hsync_start; - vspw = mode->vsync_end - mode->vsync_start; - vbp = mode->vtotal - mode->vsync_start; - vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace); - start_delay = START_DELAY(vbl); - - /* Leave reset */ - error = hwreset_get_by_ofw_name(sc->dev, 0, "lcd", &rst); - if (error != 0) { - device_printf(sc->dev, "cannot find reset 'lcd'\n"); - return (error); - } - error = hwreset_deassert(rst); - if (error != 0) { - device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n"); - return (error); - } - /* Gating AHB clock for LCD */ - error = clk_get_by_ofw_name(sc->dev, 0, "ahb_lcd", &clk_ahb); - if (error != 0) { - device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n"); - return (error); - } - error = clk_enable(clk_ahb); - if (error != 0) { - device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n"); - return (error); - } - - /* Disable TCON and TCON1 */ - TCON_WRITE(sc, TCON_GCTL, 0); - TCON_WRITE(sc, TCON1_CTL, 0); - - /* Enable clocks */ - TCON_WRITE(sc, TCON0_DCLK, DCLK_EN); - - /* Disable IO and data output ports */ - TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK); - - /* Disable TCON and select TCON1 */ - TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1); - - /* Source width and height */ - TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height)); - /* Scaler width and height */ - TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height)); - /* Output width and height */ - TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height)); - /* Horizontal total and back porch */ - TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp)); - /* Vertical total and back porch */ - vtotal = VTOTAL(mode->vtotal); - if (interlace) { - framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock), - mode->htotal), mode->vtotal); - clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate; - if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock)) - vtotal += 1; - } - TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp)); - /* Horizontal and vertical sync */ - TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw)); - /* Polarity */ - val = IO_POL_IO2_INV; - if (mode->flags & VID_PHSYNC) - val |= IO_POL_PHSYNC; - if (mode->flags & VID_PVSYNC) - val |= IO_POL_PVSYNC; - TCON_WRITE(sc, TCON1_IO_POL, val); - - /* Set scan line for TCON1 line trigger */ - TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay)); - - /* Enable TCON1 */ - val = TCON1_EN; - if (interlace) - val |= INTERLACE_EN; - val |= TCON1_START_DELAY(start_delay); - val |= TCON1_SRC_SEL(TCON1_SRC_CH1); - TCON_WRITE(sc, TCON1_CTL, val); - - /* Setup PLL */ - return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock))); -} - -static void -a10fb_enable_tcon(struct a10fb_softc *sc, int onoff) -{ - uint32_t val; - - /* Enable TCON */ - val = TCON_READ(sc, TCON_GCTL); - if (onoff) - val |= GCTL_TCON_EN; - else - val &= ~GCTL_TCON_EN; - TCON_WRITE(sc, TCON_GCTL, val); - - /* Enable TCON1 IO0/IO1 outputs */ - val = TCON_READ(sc, TCON1_IO_TRI); - if (onoff) - val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN); - else - val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN); - TCON_WRITE(sc, TCON1_IO_TRI, val); -} - -static int -a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode) -{ - size_t fbsize; - int error; - - fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY)); - - /* Detach the old FB device */ - if (sc->fbdev != NULL) { - device_delete_child(sc->dev, sc->fbdev); - sc->fbdev = NULL; - } - - /* If the FB size has changed, free the old FB memory */ - if (sc->fbsize > 0 && sc->fbsize != fbsize) { - a10fb_freefb(sc); - sc->vaddr = 0; - } - - /* Allocate the FB if necessary */ - sc->fbsize = fbsize; - if (sc->vaddr == 0) { - error = a10fb_allocfb(sc); - if (error != 0) { - device_printf(sc->dev, "failed to allocate FB memory\n"); - return (ENXIO); - } - } - - /* Setup display backend */ - error = a10fb_setup_debe(sc, mode); - if (error != 0) - return (error); - - /* Setup display timing controller */ - error = a10fb_setup_tcon(sc, mode); - if (error != 0) - return (error); - - /* Attach framebuffer device */ - sc->info.fb_name = device_get_nameunit(sc->dev); - sc->info.fb_vbase = (intptr_t)sc->vaddr; - sc->info.fb_pbase = sc->paddr; - sc->info.fb_size = sc->fbsize; - sc->info.fb_bpp = sc->info.fb_depth = FB_BPP; - sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY); - sc->info.fb_width = mode->hdisplay; - sc->info.fb_height = mode->vdisplay; - - sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev)); - if (sc->fbdev == NULL) { - device_printf(sc->dev, "failed to add fbd child\n"); - return (ENOENT); - } - - error = device_probe_and_attach(sc->fbdev); - if (error != 0) { - device_printf(sc->dev, "failed to attach fbd device\n"); - return (error); - } - - return (0); -} - -static void -a10fb_hdmi_event(void *arg, device_t hdmi_dev) -{ - const struct videomode *mode; - struct videomode hdmi_mode; - struct a10fb_softc *sc; - struct edid_info ei; - uint8_t *edid; - uint32_t edid_len; - int error; - - sc = arg; - edid = NULL; - edid_len = 0; - mode = NULL; - - error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len); - if (error != 0) { - device_printf(sc->dev, "failed to get EDID: %d\n", error); - } else { - error = edid_parse(edid, &ei); - if (error != 0) { - device_printf(sc->dev, "failed to parse EDID: %d\n", - error); - } else { - if (bootverbose) - edid_print(&ei); - mode = ei.edid_preferred_mode; - } - } - - /* If the preferred mode could not be determined, use the default */ - if (mode == NULL) - mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H, - FB_DEFAULT_REF); - - if (mode == NULL) { - device_printf(sc->dev, "failed to find usable video mode\n"); - return; - } - - if (bootverbose) - device_printf(sc->dev, "using %dx%d\n", - mode->hdisplay, mode->vdisplay); - - /* Disable HDMI */ - HDMI_ENABLE(hdmi_dev, 0); - - /* Disable timing controller */ - a10fb_enable_tcon(sc, 0); - - /* Configure DEBE and TCON */ - error = a10fb_configure(sc, mode); - if (error != 0) { - device_printf(sc->dev, "failed to configure FB: %d\n", error); - return; - } - - hdmi_mode = *mode; - hdmi_mode.hskew = mode->hsync_end - mode->hsync_start; - hdmi_mode.flags |= VID_HSKEW; - HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode); - - /* Enable timing controller */ - a10fb_enable_tcon(sc, 1); - - DELAY(HDMI_ENABLE_DELAY); - - /* Enable HDMI */ - HDMI_ENABLE(hdmi_dev, 1); -} - -static int -a10fb_probe(device_t dev) -{ - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb")) - return (ENXIO); - - device_set_desc(dev, "Allwinner Framebuffer"); - return (BUS_PROBE_DEFAULT); -} - -static int -a10fb_attach(device_t dev) -{ - struct a10fb_softc *sc; - - sc = device_get_softc(dev); - - sc->dev = dev; - - if (bus_alloc_resources(dev, a10fb_spec, sc->res)) { - device_printf(dev, "cannot allocate resources for device\n"); - return (ENXIO); - } - - sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event, - a10fb_hdmi_event, sc, 0); - - return (0); -} - -static struct fb_info * -a10fb_fb_getinfo(device_t dev) -{ - struct a10fb_softc *sc; - - sc = device_get_softc(dev); - - return (&sc->info); -} - -static device_method_t a10fb_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, a10fb_probe), - DEVMETHOD(device_attach, a10fb_attach), - - /* FB interface */ - DEVMETHOD(fb_getinfo, a10fb_fb_getinfo), - - DEVMETHOD_END -}; - -static driver_t a10fb_driver = { - "fb", - a10fb_methods, - sizeof(struct a10fb_softc), -}; - -DRIVER_MODULE(fb, simplebus, a10fb_driver, 0, 0); diff --git a/sys/arm/allwinner/a10_hdmi.c b/sys/arm/allwinner/a10_hdmi.c deleted file mode 100644 index ee57c07737de..000000000000 --- a/sys/arm/allwinner/a10_hdmi.c +++ /dev/null @@ -1,723 +0,0 @@ -/*- - * Copyright (c) 2016 Jared McNeill - * - * 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 ``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 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. - * - * $FreeBSD$ - */ - -/* - * Allwinner A10/A20 HDMI TX - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - -#include - -#include "hdmi_if.h" - -#define HDMI_CTRL 0x004 -#define CTRL_MODULE_EN (1 << 31) -#define HDMI_INT_STATUS 0x008 -#define HDMI_HPD 0x00c -#define HPD_DET (1 << 0) -#define HDMI_VID_CTRL 0x010 -#define VID_CTRL_VIDEO_EN (1 << 31) -#define VID_CTRL_HDMI_MODE (1 << 30) -#define VID_CTRL_INTERLACE (1 << 4) -#define VID_CTRL_REPEATER_2X (1 << 0) -#define HDMI_VID_TIMING0 0x014 -#define VID_ACT_V(v) (((v) - 1) << 16) -#define VID_ACT_H(h) (((h) - 1) << 0) -#define HDMI_VID_TIMING1 0x018 -#define VID_VBP(vbp) (((vbp) - 1) << 16) -#define VID_HBP(hbp) (((hbp) - 1) << 0) -#define HDMI_VID_TIMING2 0x01c -#define VID_VFP(vfp) (((vfp) - 1) << 16) -#define VID_HFP(hfp) (((hfp) - 1) << 0) -#define HDMI_VID_TIMING3 0x020 -#define VID_VSPW(vspw) (((vspw) - 1) << 16) -#define VID_HSPW(hspw) (((hspw) - 1) << 0) -#define HDMI_VID_TIMING4 0x024 -#define TX_CLOCK_NORMAL 0x03e00000 -#define VID_VSYNC_ACTSEL (1 << 1) -#define VID_HSYNC_ACTSEL (1 << 0) -#define HDMI_AUD_CTRL 0x040 -#define AUD_CTRL_EN (1 << 31) -#define AUD_CTRL_RST (1 << 30) -#define HDMI_ADMA_CTRL 0x044 -#define HDMI_ADMA_MODE (1 << 31) -#define HDMI_ADMA_MODE_DDMA (0 << 31) -#define HDMI_ADMA_MODE_NDMA (1 << 31) -#define HDMI_AUD_FMT 0x048 -#define AUD_FMT_CH(n) ((n) - 1) -#define HDMI_PCM_CTRL 0x04c -#define HDMI_AUD_CTS 0x050 -#define HDMI_AUD_N 0x054 -#define HDMI_AUD_CH_STATUS0 0x058 -#define CH_STATUS0_FS_FREQ (0xf << 24) -#define CH_STATUS0_FS_FREQ_48 (2 << 24) -#define HDMI_AUD_CH_STATUS1 0x05c -#define CH_STATUS1_WORD_LEN (0x7 << 1) -#define CH_STATUS1_WORD_LEN_16 (1 << 1) -#define HDMI_AUDIO_RESET_RETRY 1000 -#define HDMI_AUDIO_CHANNELS 2 -#define HDMI_AUDIO_CHANNELMAP 0x76543210 -#define HDMI_AUDIO_N 6144 /* 48 kHz */ -#define HDMI_AUDIO_CTS(r, n) ((((r) * 10) * ((n) / 128)) / 480) -#define HDMI_PADCTRL0 0x200 -#define PADCTRL0_BIASEN (1 << 31) -#define PADCTRL0_LDOCEN (1 << 30) -#define PADCTRL0_LDODEN (1 << 29) -#define PADCTRL0_PWENC (1 << 28) -#define PADCTRL0_PWEND (1 << 27) -#define PADCTRL0_PWENG (1 << 26) -#define PADCTRL0_CKEN (1 << 25) -#define PADCTRL0_SEN (1 << 24) -#define PADCTRL0_TXEN (1 << 23) -#define HDMI_PADCTRL1 0x204 -#define PADCTRL1_AMP_OPT (1 << 23) -#define PADCTRL1_AMPCK_OPT (1 << 22) -#define PADCTRL1_DMP_OPT (1 << 21) -#define PADCTRL1_EMP_OPT (1 << 20) -#define PADCTRL1_EMPCK_OPT (1 << 19) -#define PADCTRL1_PWSCK (1 << 18) -#define PADCTRL1_PWSDT (1 << 17) -#define PADCTRL1_REG_CSMPS (1 << 16) -#define PADCTRL1_REG_DEN (1 << 15) -#define PADCTRL1_REG_DENCK (1 << 14) -#define PADCTRL1_REG_PLRCK (1 << 13) -#define PADCTRL1_REG_EMP (0x7 << 10) -#define PADCTRL1_REG_EMP_EN (0x2 << 10) -#define PADCTRL1_REG_CD (0x3 << 8) -#define PADCTRL1_REG_CKSS (0x3 << 6) -#define PADCTRL1_REG_CKSS_1X (0x1 << 6) -#define PADCTRL1_REG_CKSS_2X (0x0 << 6) -#define PADCTRL1_REG_AMP (0x7 << 3) -#define PADCTRL1_REG_AMP_EN (0x6 << 3) -#define PADCTRL1_REG_PLR (0x7 << 0) -#define HDMI_PLLCTRL0 0x208 -#define PLLCTRL0_PLL_EN (1 << 31) -#define PLLCTRL0_BWS (1 << 30) -#define PLLCTRL0_HV_IS_33 (1 << 29) -#define PLLCTRL0_LDO1_EN (1 << 28) -#define PLLCTRL0_LDO2_EN (1 << 27) -#define PLLCTRL0_SDIV2 (1 << 25) -#define PLLCTRL0_VCO_GAIN (0x1 << 22) -#define PLLCTRL0_S (0x7 << 17) -#define PLLCTRL0_CP_S (0xf << 12) -#define PLLCTRL0_CS (0x7 << 8) -#define PLLCTRL0_PREDIV(x) ((x) << 4) -#define PLLCTRL0_VCO_S (0x8 << 0) -#define HDMI_PLLDBG0 0x20c -#define PLLDBG0_CKIN_SEL (1 << 21) -#define PLLDBG0_CKIN_SEL_PLL3 (0 << 21) -#define PLLDBG0_CKIN_SEL_PLL7 (1 << 21) -#define HDMI_PKTCTRL0 0x2f0 -#define HDMI_PKTCTRL1 0x2f4 -#define PKTCTRL_PACKET(n,t) ((t) << ((n) << 2)) -#define PKT_NULL 0 -#define PKT_GC 1 -#define PKT_AVI 2 -#define PKT_AI 3 -#define PKT_SPD 5 -#define PKT_END 15 -#define DDC_CTRL 0x500 -#define CTRL_DDC_EN (1 << 31) -#define CTRL_DDC_ACMD_START (1 << 30) -#define CTRL_DDC_FIFO_DIR (1 << 8) -#define CTRL_DDC_FIFO_DIR_READ (0 << 8) -#define CTRL_DDC_FIFO_DIR_WRITE (1 << 8) -#define CTRL_DDC_SWRST (1 << 0) -#define DDC_SLAVE_ADDR 0x504 -#define SLAVE_ADDR_SEG_SHIFT 24 -#define SLAVE_ADDR_EDDC_SHIFT 16 -#define SLAVE_ADDR_OFFSET_SHIFT 8 -#define SLAVE_ADDR_SHIFT 0 -#define DDC_INT_STATUS 0x50c -#define INT_STATUS_XFER_DONE (1 << 0) -#define DDC_FIFO_CTRL 0x510 -#define FIFO_CTRL_CLEAR (1 << 31) -#define DDC_BYTE_COUNTER 0x51c -#define DDC_COMMAND 0x520 -#define COMMAND_EOREAD (4 << 0) -#define DDC_CLOCK 0x528 -#define DDC_CLOCK_M (1 << 3) -#define DDC_CLOCK_N (5 << 0) -#define DDC_FIFO 0x518 -#define SWRST_DELAY 1000 -#define DDC_DELAY 1000 -#define DDC_RETRY 1000 -#define DDC_BLKLEN 16 -#define DDC_ADDR 0x50 -#define EDDC_ADDR 0x60 -#define EDID_LENGTH 128 -#define DDC_CTRL_LINE 0x540 -#define DDC_LINE_SCL_ENABLE (1 << 8) -#define DDC_LINE_SDA_ENABLE (1 << 9) -#define HDMI_ENABLE_DELAY 50000 -#define DDC_READ_RETRY 4 -#define EXT_TAG 0x00 -#define CEA_TAG_ID 0x02 -#define CEA_DTD 0x03 -#define DTD_BASIC_AUDIO (1 << 6) -#define CEA_REV 0x02 -#define CEA_DATA_OFF 0x03 -#define CEA_DATA_START 4 -#define BLOCK_TAG(x) (((x) >> 5) & 0x7) -#define BLOCK_TAG_VSDB 3 -#define BLOCK_LEN(x) ((x) & 0x1f) -#define HDMI_VSDB_MINLEN 5 -#define HDMI_OUI "\x03\x0c\x00" -#define HDMI_OUI_LEN 3 -#define HDMI_DEFAULT_FREQ 297000000 - -struct a10hdmi_softc { - struct resource *res; - - struct intr_config_hook mode_hook; - - uint8_t edid[EDID_LENGTH]; - - int has_hdmi; - int has_audio; - - clk_t clk_ahb; - clk_t clk_hdmi; - clk_t clk_lcd; -}; - -static struct resource_spec a10hdmi_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { -1, 0 } -}; - -#define HDMI_READ(sc, reg) bus_read_4((sc)->res, (reg)) -#define HDMI_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) - -static void -a10hdmi_init(struct a10hdmi_softc *sc) -{ - /* Enable the HDMI module */ - HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN); - - /* Configure PLL/DRV settings */ - HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN | - PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND | - PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN); - HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT | - PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN | - PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN); - - /* Select PLL3 as input clock */ - HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3); - - DELAY(HDMI_ENABLE_DELAY); -} - -static void -a10hdmi_hpd(void *arg) -{ - struct a10hdmi_softc *sc; - device_t dev; - uint32_t hpd; - - dev = arg; - sc = device_get_softc(dev); - - hpd = HDMI_READ(sc, HDMI_HPD); - if ((hpd & HPD_DET) == HPD_DET) - EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED); - - config_intrhook_disestablish(&sc->mode_hook); -} - -static int -a10hdmi_probe(device_t dev) -{ - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi")) - return (ENXIO); - - device_set_desc(dev, "Allwinner HDMI TX"); - return (BUS_PROBE_DEFAULT); -} - -static int -a10hdmi_attach(device_t dev) -{ - struct a10hdmi_softc *sc; - int error; - - sc = device_get_softc(dev); - - if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) { - device_printf(dev, "cannot allocate resources for device\n"); - return (ENXIO); - } - - /* Setup clocks */ - error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb); - if (error != 0) { - device_printf(dev, "cannot find ahb clock\n"); - return (error); - } - error = clk_get_by_ofw_name(dev, 0, "hdmi", &sc->clk_hdmi); - if (error != 0) { - device_printf(dev, "cannot find hdmi clock\n"); - return (error); - } - error = clk_get_by_ofw_name(dev, 0, "lcd", &sc->clk_lcd); - if (error != 0) { - device_printf(dev, "cannot find lcd clock\n"); - } - /* Enable HDMI clock */ - error = clk_enable(sc->clk_hdmi); - if (error != 0) { - device_printf(dev, "cannot enable hdmi clock\n"); - return (error); - } - /* Gating AHB clock for HDMI */ - error = clk_enable(sc->clk_ahb); - if (error != 0) { - device_printf(dev, "cannot enable ahb gate\n"); - return (error); - } - - a10hdmi_init(sc); - - sc->mode_hook.ich_func = a10hdmi_hpd; - sc->mode_hook.ich_arg = dev; - - error = config_intrhook_establish(&sc->mode_hook); - if (error != 0) - return (error); - - return (0); -} - -static int -a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg, - uint8_t off, int len) -{ - uint32_t val; - int retry; - - /* Set FIFO direction to read */ - val = HDMI_READ(sc, DDC_CTRL); - val &= ~CTRL_DDC_FIFO_DIR; - val |= CTRL_DDC_FIFO_DIR_READ; - HDMI_WRITE(sc, DDC_CTRL, val); - - /* Setup DDC slave address */ - val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) | - (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) | - (off << SLAVE_ADDR_OFFSET_SHIFT); - HDMI_WRITE(sc, DDC_SLAVE_ADDR, val); - - /* Clear FIFO */ - val = HDMI_READ(sc, DDC_FIFO_CTRL); - val |= FIFO_CTRL_CLEAR; - HDMI_WRITE(sc, DDC_FIFO_CTRL, val); - - /* Set transfer length */ - HDMI_WRITE(sc, DDC_BYTE_COUNTER, len); - - /* Set command to "Explicit Offset Address Read" */ - HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD); - - /* Start transfer */ - val = HDMI_READ(sc, DDC_CTRL); - val |= CTRL_DDC_ACMD_START; - HDMI_WRITE(sc, DDC_CTRL, val); - - /* Wait for command to start */ - retry = DDC_RETRY; - while (--retry > 0) { - val = HDMI_READ(sc, DDC_CTRL); - if ((val & CTRL_DDC_ACMD_START) == 0) - break; - DELAY(DDC_DELAY); - } - if (retry == 0) - return (ETIMEDOUT); - - /* Ensure that the transfer completed */ - val = HDMI_READ(sc, DDC_INT_STATUS); - if ((val & INT_STATUS_XFER_DONE) == 0) - return (EIO); - - return (0); -} - -static int -a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid) -{ - int resid, off, len, error; - uint8_t *pbuf; - - pbuf = edid; - resid = EDID_LENGTH; - off = (block & 1) ? EDID_LENGTH : 0; - - while (resid > 0) { - len = min(resid, DDC_BLKLEN); - error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len); - if (error != 0) - return (error); - - bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len); - - pbuf += len; - off += len; - resid -= len; - } - - return (0); -} - -static int -a10hdmi_detect_hdmi_vsdb(uint8_t *edid) -{ - int off, p, btag, blen; - - if (edid[EXT_TAG] != CEA_TAG_ID) - return (0); - - off = edid[CEA_DATA_OFF]; - - /* CEA data block collection starts at byte 4 */ - if (off <= CEA_DATA_START) - return (0); - - /* Parse the CEA data blocks */ - for (p = CEA_DATA_START; p < off;) { - btag = BLOCK_TAG(edid[p]); - blen = BLOCK_LEN(edid[p]); - - /* Make sure the length is sane */ - if (p + blen + 1 > off) - break; - - /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */ - if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN && - memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0) - return (1); - - /* Next data block */ - p += (1 + blen); - } - - return (0); -} - -static void -a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio) -{ - struct edid_info ei; - uint8_t edid[EDID_LENGTH]; - int block; - - *phdmi = *paudio = 0; - - if (edid_parse(sc->edid, &ei) != 0) - return; - - /* Scan through extension blocks, looking for a CEA-861 block. */ - for (block = 1; block <= ei.edid_ext_block_count; block++) { - if (a10hdmi_ddc_read(sc, block, edid) != 0) - return; - - if (a10hdmi_detect_hdmi_vsdb(edid) != 0) { - *phdmi = 1; - *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); - return; - } - } -} - -static int -a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len) -{ - struct a10hdmi_softc *sc; - int error, retry; - - sc = device_get_softc(dev); - retry = DDC_READ_RETRY; - - while (--retry > 0) { - /* I2C software reset */ - HDMI_WRITE(sc, DDC_FIFO_CTRL, 0); - HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST); - DELAY(SWRST_DELAY); - if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) { - device_printf(dev, "DDC software reset failed\n"); - return (ENXIO); - } - - /* Configure DDC clock */ - HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N); - - /* Enable SDA/SCL */ - HDMI_WRITE(sc, DDC_CTRL_LINE, - DDC_LINE_SCL_ENABLE | DDC_LINE_SDA_ENABLE); - - /* Read EDID block */ - error = a10hdmi_ddc_read(sc, 0, sc->edid); - if (error == 0) { - *edid = sc->edid; - *edid_len = sizeof(sc->edid); - break; - } - } - - if (error == 0) - a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio); - else - sc->has_hdmi = sc->has_audio = 0; - - return (error); -} - -static void -a10hdmi_set_audiomode(device_t dev, const struct videomode *mode) -{ - struct a10hdmi_softc *sc; - uint32_t val; - int retry; - - sc = device_get_softc(dev); - - /* Disable and reset audio module and wait for reset bit to clear */ - HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST); - for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) { - val = HDMI_READ(sc, HDMI_AUD_CTRL); - if ((val & AUD_CTRL_RST) == 0) - break; - } - if (retry == 0) { - device_printf(dev, "timeout waiting for audio module\n"); - return; - } - - if (!sc->has_audio) - return; - - /* DMA and FIFO control */ - HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA); - - /* Audio format control (LPCM, S16LE, stereo) */ - HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS)); - - /* Channel mappings */ - HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP); - - /* Clocks */ - HDMI_WRITE(sc, HDMI_AUD_CTS, - HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N)); - HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N); - - /* Set sampling frequency to 48 kHz, word length to 16-bit */ - HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48); - HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16); - - /* Enable */ - HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN); -} - -static int -a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl) -{ - uint64_t lcd_fin, lcd_fout; - clk_t clk_lcd_parent; - const char *pname; - int error; - - error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent); - if (error != 0) - return (error); - - /* Get the LCD CH1 special clock 2 divider */ - error = clk_get_freq(sc->clk_lcd, &lcd_fout); - if (error != 0) - return (error); - error = clk_get_freq(clk_lcd_parent, &lcd_fin); - if (error != 0) - return (error); - *div = lcd_fin / lcd_fout; - - /* Detect LCD CH1 special clock using a 1X or 2X source */ - /* XXX */ - pname = clk_get_name(clk_lcd_parent); - if (strcmp(pname, "pll3") == 0 || strcmp(pname, "pll7") == 0) - *dbl = 0; - else - *dbl = 1; - - return (0); -} - -static int -a10hdmi_set_videomode(device_t dev, const struct videomode *mode) -{ - struct a10hdmi_softc *sc; - int error, clk_div, clk_dbl; - int dblscan, hfp, hspw, hbp, vfp, vspw, vbp; - uint32_t val; - - sc = device_get_softc(dev); - dblscan = !!(mode->flags & VID_DBLSCAN); - hfp = mode->hsync_start - mode->hdisplay; - hspw = mode->hsync_end - mode->hsync_start; - hbp = mode->htotal - mode->hsync_start; - vfp = mode->vsync_start - mode->vdisplay; - vspw = mode->vsync_end - mode->vsync_start; - vbp = mode->vtotal - mode->vsync_start; - - error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl); - if (error != 0) { - device_printf(dev, "couldn't get tcon config: %d\n", error); - return (error); - } - - /* Clear interrupt status */ - HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS)); - - /* Clock setup */ - val = HDMI_READ(sc, HDMI_PADCTRL1); - val &= ~PADCTRL1_REG_CKSS; - val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X); - HDMI_WRITE(sc, HDMI_PADCTRL1, val); - HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS | - PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN | - PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S | - PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) | - PLLCTRL0_VCO_S); - - /* Setup display settings */ - if (bootverbose) - device_printf(dev, "HDMI: %s, Audio: %s\n", - sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no"); - val = 0; - if (sc->has_hdmi) - val |= VID_CTRL_HDMI_MODE; - if (mode->flags & VID_INTERLACE) - val |= VID_CTRL_INTERLACE; - if (mode->flags & VID_DBLSCAN) - val |= VID_CTRL_REPEATER_2X; - HDMI_WRITE(sc, HDMI_VID_CTRL, val); - - /* Setup display timings */ - HDMI_WRITE(sc, HDMI_VID_TIMING0, - VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan)); - HDMI_WRITE(sc, HDMI_VID_TIMING1, - VID_VBP(vbp) | VID_HBP(hbp << dblscan)); - HDMI_WRITE(sc, HDMI_VID_TIMING2, - VID_VFP(vfp) | VID_HFP(hfp << dblscan)); - HDMI_WRITE(sc, HDMI_VID_TIMING3, - VID_VSPW(vspw) | VID_HSPW(hspw << dblscan)); - val = TX_CLOCK_NORMAL; - if (mode->flags & VID_PVSYNC) - val |= VID_VSYNC_ACTSEL; - if (mode->flags & VID_PHSYNC) - val |= VID_HSYNC_ACTSEL; - HDMI_WRITE(sc, HDMI_VID_TIMING4, val); - - /* This is an ordered list of infoframe packets that the HDMI - * transmitter will send. Transmit packets in the following order: - * 1. General control packet - * 2. AVI infoframe - * 3. Audio infoframe - * There are 2 registers with 4 slots each. The list is terminated - * with the special PKT_END marker. - */ - HDMI_WRITE(sc, HDMI_PKTCTRL0, - PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) | - PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END)); - HDMI_WRITE(sc, HDMI_PKTCTRL1, 0); - - /* Setup audio */ - a10hdmi_set_audiomode(dev, mode); - - return (0); -} - -static int -a10hdmi_enable(device_t dev, int onoff) -{ - struct a10hdmi_softc *sc; - uint32_t val; - - sc = device_get_softc(dev); - - /* Enable or disable video output */ - val = HDMI_READ(sc, HDMI_VID_CTRL); - if (onoff) - val |= VID_CTRL_VIDEO_EN; - else - val &= ~VID_CTRL_VIDEO_EN; - HDMI_WRITE(sc, HDMI_VID_CTRL, val); - - return (0); -} - -static device_method_t a10hdmi_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, a10hdmi_probe), - DEVMETHOD(device_attach, a10hdmi_attach), - - /* HDMI interface */ - DEVMETHOD(hdmi_get_edid, a10hdmi_get_edid), - DEVMETHOD(hdmi_set_videomode, a10hdmi_set_videomode), - DEVMETHOD(hdmi_enable, a10hdmi_enable), - - DEVMETHOD_END -}; - -static driver_t a10hdmi_driver = { - "a10hdmi", - a10hdmi_methods, - sizeof(struct a10hdmi_softc), -}; - -DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, 0, 0); -MODULE_VERSION(a10hdmi, 1); diff --git a/sys/arm/allwinner/a10_hdmiaudio.c b/sys/arm/allwinner/a10_hdmiaudio.c deleted file mode 100644 index 13fe2ea7f1b7..000000000000 --- a/sys/arm/allwinner/a10_hdmiaudio.c +++ /dev/null @@ -1,435 +0,0 @@ -/*- - * Copyright (c) 2016 Jared McNeill - * - * 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 ``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 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. - * - * $FreeBSD$ - */ - -/* - * Allwinner A10/A20 HDMI Audio - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "sunxi_dma_if.h" -#include "mixer_if.h" - -#define DRQTYPE_HDMIAUDIO 24 -#define DRQTYPE_SDRAM 1 - -#define DMA_WIDTH 32 -#define DMA_BURST_LEN 8 -#define DDMA_BLKSIZE 32 -#define DDMA_WAIT_CYC 8 - -#define DMABUF_MIN 4096 -#define DMABUF_DEFAULT 65536 -#define DMABUF_MAX 131072 - -#define HDMI_SAMPLERATE 48000 - -#define TX_FIFO 0x01c16400 - -static uint32_t a10hdmiaudio_fmt[] = { - SND_FORMAT(AFMT_S16_LE, 2, 0), - 0 -}; - -static struct pcmchan_caps a10hdmiaudio_pcaps = { - HDMI_SAMPLERATE, HDMI_SAMPLERATE, a10hdmiaudio_fmt, 0 -}; - -struct a10hdmiaudio_info; - -struct a10hdmiaudio_chinfo { - struct snd_dbuf *buffer; - struct pcm_channel *channel; - struct a10hdmiaudio_info *parent; - bus_dmamap_t dmamap; - void *dmaaddr; - bus_addr_t physaddr; - device_t dmac; - void *dmachan; - - int run; - uint32_t pos; - uint32_t blocksize; -}; - -struct a10hdmiaudio_info { - device_t dev; - struct mtx *lock; - bus_dma_tag_t dmat; - unsigned dmasize; - - struct a10hdmiaudio_chinfo play; -}; - -/* - * Mixer interface - */ - -static int -a10hdmiaudio_mixer_init(struct snd_mixer *m) -{ - mix_setdevs(m, SOUND_MASK_PCM); - - return (0); -} - -static int -a10hdmiaudio_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, - unsigned right) -{ - return (-1); -} - -static kobj_method_t a10hdmiaudio_mixer_methods[] = { - KOBJMETHOD(mixer_init, a10hdmiaudio_mixer_init), - KOBJMETHOD(mixer_set, a10hdmiaudio_mixer_set), - KOBJMETHOD_END -}; -MIXER_DECLARE(a10hdmiaudio_mixer); - -/* - * Channel interface - */ - -static void -a10hdmiaudio_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) -{ - struct a10hdmiaudio_chinfo *ch = arg; - - if (error != 0) - return; - - ch->physaddr = segs[0].ds_addr; -} - -static void -a10hdmiaudio_transfer(struct a10hdmiaudio_chinfo *ch) -{ - int error; - - error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, - ch->physaddr + ch->pos, TX_FIFO, ch->blocksize); - if (error) { - ch->run = 0; - device_printf(ch->parent->dev, "DMA transfer failed: %d\n", - error); - } -} - -static void -a10hdmiaudio_dmaconfig(struct a10hdmiaudio_chinfo *ch) -{ - struct sunxi_dma_config conf; - - memset(&conf, 0, sizeof(conf)); - conf.src_width = conf.dst_width = DMA_WIDTH; - conf.src_burst_len = conf.dst_burst_len = DMA_BURST_LEN; - conf.src_blksize = conf.dst_blksize = DDMA_BLKSIZE; - conf.src_wait_cyc = conf.dst_wait_cyc = DDMA_WAIT_CYC; - conf.src_drqtype = DRQTYPE_SDRAM; - conf.dst_drqtype = DRQTYPE_HDMIAUDIO; - conf.dst_noincr = true; - - SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf); -} - -static void -a10hdmiaudio_dmaintr(void *priv) -{ - struct a10hdmiaudio_chinfo *ch = priv; - unsigned bufsize; - - bufsize = sndbuf_getsize(ch->buffer); - - ch->pos += ch->blocksize; - if (ch->pos >= bufsize) - ch->pos -= bufsize; - - if (ch->run) { - chn_intr(ch->channel); - a10hdmiaudio_transfer(ch); - } -} - -static void -a10hdmiaudio_start(struct a10hdmiaudio_chinfo *ch) -{ - ch->pos = 0; - - /* Configure DMA channel */ - a10hdmiaudio_dmaconfig(ch); - - /* Start DMA transfer */ - a10hdmiaudio_transfer(ch); -} - -static void -a10hdmiaudio_stop(struct a10hdmiaudio_chinfo *ch) -{ - /* Disable DMA channel */ - SUNXI_DMA_HALT(ch->dmac, ch->dmachan); -} - -static void * -a10hdmiaudio_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, - struct pcm_channel *c, int dir) -{ - struct a10hdmiaudio_info *sc = devinfo; - struct a10hdmiaudio_chinfo *ch = &sc->play; - int error; - - ch->parent = sc; - ch->channel = c; - ch->buffer = b; - - ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0); - if (ch->dmac == NULL) { - device_printf(sc->dev, "cannot find DMA controller\n"); - return (NULL); - } - ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, true, a10hdmiaudio_dmaintr, ch); - if (ch->dmachan == NULL) { - device_printf(sc->dev, "cannot allocate DMA channel\n"); - return (NULL); - } - - error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr, - BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap); - if (error != 0) { - device_printf(sc->dev, "cannot allocate channel buffer\n"); - return (NULL); - } - error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr, - sc->dmasize, a10hdmiaudio_dmamap_cb, ch, BUS_DMA_NOWAIT); - if (error != 0) { - device_printf(sc->dev, "cannot load DMA map\n"); - return (NULL); - } - memset(ch->dmaaddr, 0, sc->dmasize); - - if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) { - device_printf(sc->dev, "cannot setup sndbuf\n"); - return (NULL); - } - - return (ch); -} - -static int -a10hdmiaudio_chan_free(kobj_t obj, void *data) -{ - struct a10hdmiaudio_chinfo *ch = data; - struct a10hdmiaudio_info *sc = ch->parent; - - SUNXI_DMA_FREE(ch->dmac, ch->dmachan); - bus_dmamap_unload(sc->dmat, ch->dmamap); - bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap); - - return (0); -} - -static int -a10hdmiaudio_chan_setformat(kobj_t obj, void *data, uint32_t format) -{ - return (0); -} - -static uint32_t -a10hdmiaudio_chan_setspeed(kobj_t obj, void *data, uint32_t speed) -{ - return (HDMI_SAMPLERATE); -} - -static uint32_t -a10hdmiaudio_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) -{ - struct a10hdmiaudio_chinfo *ch = data; - - ch->blocksize = blocksize & ~3; - - return (ch->blocksize); -} - -static int -a10hdmiaudio_chan_trigger(kobj_t obj, void *data, int go) -{ - struct a10hdmiaudio_chinfo *ch = data; - struct a10hdmiaudio_info *sc = ch->parent; - - if (!PCMTRIG_COMMON(go)) - return (0); - - snd_mtxlock(sc->lock); - switch (go) { - case PCMTRIG_START: - ch->run = 1; - a10hdmiaudio_start(ch); - break; - case PCMTRIG_STOP: - case PCMTRIG_ABORT: - ch->run = 0; - a10hdmiaudio_stop(ch); - break; - default: - break; - } - snd_mtxunlock(sc->lock); - - return (0); -} - -static uint32_t -a10hdmiaudio_chan_getptr(kobj_t obj, void *data) -{ - struct a10hdmiaudio_chinfo *ch = data; - - return (ch->pos); -} - -static struct pcmchan_caps * -a10hdmiaudio_chan_getcaps(kobj_t obj, void *data) -{ - return (&a10hdmiaudio_pcaps); -} - -static kobj_method_t a10hdmiaudio_chan_methods[] = { - KOBJMETHOD(channel_init, a10hdmiaudio_chan_init), - KOBJMETHOD(channel_free, a10hdmiaudio_chan_free), - KOBJMETHOD(channel_setformat, a10hdmiaudio_chan_setformat), - KOBJMETHOD(channel_setspeed, a10hdmiaudio_chan_setspeed), - KOBJMETHOD(channel_setblocksize, a10hdmiaudio_chan_setblocksize), - KOBJMETHOD(channel_trigger, a10hdmiaudio_chan_trigger), - KOBJMETHOD(channel_getptr, a10hdmiaudio_chan_getptr), - KOBJMETHOD(channel_getcaps, a10hdmiaudio_chan_getcaps), - KOBJMETHOD_END -}; -CHANNEL_DECLARE(a10hdmiaudio_chan); - -/* - * Device interface - */ - -static int -a10hdmiaudio_probe(device_t dev) -{ - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmiaudio")) - return (ENXIO); - - device_set_desc(dev, "Allwinner HDMI Audio"); - return (BUS_PROBE_DEFAULT); -} - -static int -a10hdmiaudio_attach(device_t dev) -{ - struct a10hdmiaudio_info *sc; - char status[SND_STATUSLEN]; - int error; - - sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); - sc->dev = dev; - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10hdmiaudio softc"); - - sc->dmasize = pcm_getbuffersize(dev, DMABUF_MIN, - DMABUF_DEFAULT, DMABUF_MAX); - error = bus_dma_tag_create( - bus_get_dma_tag(dev), - 4, sc->dmasize, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - sc->dmasize, 1, /* maxsize, nsegs */ - sc->dmasize, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->dmat); - if (error != 0) { - device_printf(dev, "cannot create DMA tag\n"); - goto fail; - } - - if (mixer_init(dev, &a10hdmiaudio_mixer_class, sc)) { - device_printf(dev, "mixer_init failed\n"); - goto fail; - } - - pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); - pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); - - if (pcm_register(dev, sc, 1, 0)) { - device_printf(dev, "pcm_register failed\n"); - goto fail; - } - - pcm_addchan(dev, PCMDIR_PLAY, &a10hdmiaudio_chan_class, sc); - - snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); - pcm_setstatus(dev, status); - - return (0); - -fail: - snd_mtxfree(sc->lock); - free(sc, M_DEVBUF); - - return (error); -} - -static device_method_t a10hdmiaudio_pcm_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, a10hdmiaudio_probe), - DEVMETHOD(device_attach, a10hdmiaudio_attach), - - DEVMETHOD_END -}; - -static driver_t a10hdmiaudio_pcm_driver = { - "pcm", - a10hdmiaudio_pcm_methods, - PCM_SOFTC_SIZE, -}; - -DRIVER_MODULE(a10hdmiaudio, simplebus, a10hdmiaudio_pcm_driver, 0, 0); -MODULE_DEPEND(a10hdmiaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); -MODULE_VERSION(a10hdmiaudio, 1);