Date: Thu, 25 Feb 2016 20:17:19 +0000 (UTC) From: Jared McNeill <jmcneill@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r296064 - in head/sys: arm/allwinner arm/arm arm/conf boot/fdt/dts/arm Message-ID: <201602252017.u1PKHJcb070363@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jmcneill Date: Thu Feb 25 20:17:18 2016 New Revision: 296064 URL: https://svnweb.freebsd.org/changeset/base/296064 Log: Add Allwinner A20 HDMI support. The HDMI driver will attach a framebuffer device when a display is connected. If the EDID can be read and contains a preferred mode, it will be used. Otherwise the framebuffer will default to 800x600. In addition, if the EDID contains a CEA-861 extension block and the "basic audio" flag is set, audio playback at 48kHz 16-bit stereo is enabled on the controller. Reviewed by: andrew Approved by: gonzo (mentor) Differential Revision: https://reviews.freebsd.org/D5383 Added: head/sys/arm/allwinner/a10_fb.c (contents, props changed) head/sys/arm/allwinner/a10_hdmi.c (contents, props changed) head/sys/arm/allwinner/a10_hdmiaudio.c (contents, props changed) head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi (contents, props changed) Modified: head/sys/arm/allwinner/a10_clk.c head/sys/arm/allwinner/a10_clk.h head/sys/arm/allwinner/files.allwinner head/sys/arm/arm/hdmi_if.m head/sys/arm/conf/A20 head/sys/boot/fdt/dts/arm/cubieboard2.dts Modified: head/sys/arm/allwinner/a10_clk.c ============================================================================== --- head/sys/arm/allwinner/a10_clk.c Thu Feb 25 20:12:05 2016 (r296063) +++ head/sys/arm/allwinner/a10_clk.c Thu Feb 25 20:17:18 2016 (r296064) @@ -43,6 +43,18 @@ __FBSDID("$FreeBSD$"); #include "a10_clk.h" +#define TCON_PLL_WORST 1000000 +#define TCON_PLL_N_MIN 1 +#define TCON_PLL_N_MAX 15 +#define TCON_PLL_M_MIN 9 +#define TCON_PLL_M_MAX 127 +#define TCON_PLLREF_SINGLE 3000 /* kHz */ +#define TCON_PLLREF_DOUBLE 6000 /* kHz */ +#define TCON_RATE_KHZ(rate_hz) ((rate_hz) / 1000) +#define TCON_RATE_HZ(rate_khz) ((rate_khz) * 1000) +#define HDMI_DEFAULT_RATE 297000000 +#define DEBE_DEFAULT_RATE 300000000 + struct a10_ccm_softc { struct resource *res; bus_space_tag_t bst; @@ -307,6 +319,47 @@ a10_clk_pll2_set_rate(unsigned int freq) return (0); } +static int +a10_clk_pll3_set_rate(unsigned int freq) +{ + struct a10_ccm_softc *sc; + uint32_t reg_value; + int m; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + if (freq == 0) { + /* Disable PLL3 */ + ccm_write_4(sc, CCM_PLL3_CFG, 0); + return (0); + } + + m = freq / TCON_RATE_HZ(TCON_PLLREF_SINGLE); + + reg_value = CCM_PLL_CFG_ENABLE | CCM_PLL3_CFG_MODE_SEL_INT | m; + ccm_write_4(sc, CCM_PLL3_CFG, reg_value); + + return (0); +} + +static unsigned int +a10_clk_pll5x_get_rate(void) +{ + struct a10_ccm_softc *sc; + uint32_t k, n, p, reg_value; + + sc = a10_ccm_sc; + reg_value = ccm_read_4(sc, CCM_PLL5_CFG); + n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT); + k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) + + 1; + p = ((reg_value & CCM_PLL5_CFG_OUT_EXT_DIV_P) >> CCM_PLL5_CFG_OUT_EXT_DIV_P_SHIFT); + + return ((CCM_CLK_REF_FREQ * n * k) >> p); +} + int a10_clk_ahci_activate(void) { @@ -465,3 +518,190 @@ a10_clk_codec_activate(unsigned int freq return (0); } + +static void +calc_tcon_pll(int f_ref, int f_out, int *pm, int *pn) +{ + int best, m, n, f_cur, diff; + + best = TCON_PLL_WORST; + for (n = TCON_PLL_N_MIN; n <= TCON_PLL_N_MAX; n++) { + for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) { + f_cur = (m * f_ref) / n; + diff = f_out - f_cur; + if (diff > 0 && diff < best) { + best = diff; + *pm = m; + *pn = n; + } + } + } +} + +int +a10_clk_debe_activate(void) +{ + struct a10_ccm_softc *sc; + int pll_rate, clk_div; + uint32_t reg_value; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + /* Leave reset */ + reg_value = ccm_read_4(sc, CCM_BE0_SCLK); + reg_value |= CCM_BE_CLK_RESET; + ccm_write_4(sc, CCM_BE0_SCLK, reg_value); + + pll_rate = a10_clk_pll5x_get_rate(); + + clk_div = howmany(pll_rate, DEBE_DEFAULT_RATE); + + /* Set BE0 source to PLL5 (DDR external peripheral clock) */ + reg_value = CCM_BE_CLK_RESET; + reg_value |= (CCM_BE_CLK_SRC_SEL_PLL5 << CCM_BE_CLK_SRC_SEL_SHIFT); + reg_value |= (clk_div - 1); + ccm_write_4(sc, CCM_BE0_SCLK, reg_value); + + /* Gating AHB clock for BE0 */ + reg_value = ccm_read_4(sc, CCM_AHB_GATING1); + reg_value |= CCM_AHB_GATING_DE_BE0; + ccm_write_4(sc, CCM_AHB_GATING1, reg_value); + + /* Enable DRAM clock to BE0 */ + reg_value = ccm_read_4(sc, CCM_DRAM_CLK); + reg_value |= CCM_DRAM_CLK_BE0_CLK_ENABLE; + ccm_write_4(sc, CCM_DRAM_CLK, reg_value); + + /* Enable BE0 clock */ + reg_value = ccm_read_4(sc, CCM_BE0_SCLK); + reg_value |= CCM_BE_CLK_SCLK_GATING; + ccm_write_4(sc, CCM_BE0_SCLK, reg_value); + + return (0); +} + +int +a10_clk_lcd_activate(void) +{ + struct a10_ccm_softc *sc; + uint32_t reg_value; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + /* Clear LCD0 reset */ + reg_value = ccm_read_4(sc, CCM_LCD0_CH0_CLK); + reg_value |= CCM_LCD_CH0_RESET; + ccm_write_4(sc, CCM_LCD0_CH0_CLK, reg_value); + + /* Gating AHB clock for LCD0 */ + reg_value = ccm_read_4(sc, CCM_AHB_GATING1); + reg_value |= CCM_AHB_GATING_LCD0; + ccm_write_4(sc, CCM_AHB_GATING1, reg_value); + + return (0); +} + +int +a10_clk_tcon_activate(unsigned int freq) +{ + struct a10_ccm_softc *sc; + int m, n, m2, n2, f_single, f_double, dbl, src_sel; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + m = n = m2 = n2 = 0; + dbl = 0; + + calc_tcon_pll(TCON_PLLREF_SINGLE, TCON_RATE_KHZ(freq), &m, &n); + calc_tcon_pll(TCON_PLLREF_DOUBLE, TCON_RATE_KHZ(freq), &m2, &n2); + + f_single = n ? (m * TCON_PLLREF_SINGLE) / n : 0; + f_double = n2 ? (m2 * TCON_PLLREF_DOUBLE) / n2 : 0; + + if (f_double > f_single) { + dbl = 1; + m = m2; + n = n2; + } + src_sel = dbl ? CCM_LCD_CH1_SRC_SEL_PLL3_2X : CCM_LCD_CH1_SRC_SEL_PLL3; + + if (n == 0 || m == 0) + return (EINVAL); + + /* Set PLL3 to the closest possible rate */ + a10_clk_pll3_set_rate(TCON_RATE_HZ(m * TCON_PLLREF_SINGLE)); + + /* Enable LCD0 CH1 clock */ + ccm_write_4(sc, CCM_LCD0_CH1_CLK, + CCM_LCD_CH1_SCLK2_GATING | CCM_LCD_CH1_SCLK1_GATING | + (src_sel << CCM_LCD_CH1_SRC_SEL_SHIFT) | (n - 1)); + + return (0); +} + +int +a10_clk_tcon_get_config(int *pdiv, int *pdbl) +{ + struct a10_ccm_softc *sc; + uint32_t reg_value; + int src; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + reg_value = ccm_read_4(sc, CCM_LCD0_CH1_CLK); + + *pdiv = (reg_value & CCM_LCD_CH1_CLK_DIV_RATIO_M) + 1; + + src = (reg_value & CCM_LCD_CH1_SRC_SEL) >> CCM_LCD_CH1_SRC_SEL_SHIFT; + switch (src) { + case CCM_LCD_CH1_SRC_SEL_PLL3: + case CCM_LCD_CH1_SRC_SEL_PLL7: + *pdbl = 0; + break; + case CCM_LCD_CH1_SRC_SEL_PLL3_2X: + case CCM_LCD_CH1_SRC_SEL_PLL7_2X: + *pdbl = 1; + break; + } + + return (0); +} + +int +a10_clk_hdmi_activate(void) +{ + struct a10_ccm_softc *sc; + uint32_t reg_value; + int error; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + /* Set PLL3 to 297MHz */ + error = a10_clk_pll3_set_rate(HDMI_DEFAULT_RATE); + if (error != 0) + return (error); + + /* Enable HDMI clock, source PLL3 */ + reg_value = ccm_read_4(sc, CCM_HDMI_CLK); + reg_value |= CCM_HDMI_CLK_SCLK_GATING; + reg_value &= ~CCM_HDMI_CLK_SRC_SEL; + reg_value |= (CCM_HDMI_CLK_SRC_SEL_PLL3 << CCM_HDMI_CLK_SRC_SEL_SHIFT); + ccm_write_4(sc, CCM_HDMI_CLK, reg_value); + + /* Gating AHB clock for HDMI */ + reg_value = ccm_read_4(sc, CCM_AHB_GATING1); + reg_value |= CCM_AHB_GATING_HDMI; + ccm_write_4(sc, CCM_AHB_GATING1, reg_value); + + return (0); +} Modified: head/sys/arm/allwinner/a10_clk.h ============================================================================== --- head/sys/arm/allwinner/a10_clk.h Thu Feb 25 20:12:05 2016 (r296063) +++ head/sys/arm/allwinner/a10_clk.h Thu Feb 25 20:17:18 2016 (r296064) @@ -120,9 +120,14 @@ /* AHB_GATING_REG1 */ #define CCM_AHB_GATING_GMAC (1 << 17) +#define CCM_AHB_GATING_DE_BE1 (1 << 13) +#define CCM_AHB_GATING_DE_BE0 (1 << 12) +#define CCM_AHB_GATING_HDMI (1 << 11) +#define CCM_AHB_GATING_LCD1 (1 << 5) +#define CCM_AHB_GATING_LCD0 (1 << 4) /* APB1_GATING_REG */ -#define CCM_APB1_GATING_TWI (1 << 0) +#define CCM_APB1_GATING_TWI (1 << 0) #define CCM_USB_PHY (1 << 8) #define CCM_USB0_RESET (1 << 0) @@ -144,6 +149,17 @@ #define CCM_PLL2_CFG_PREDIV 0x1f #define CCM_PLL2_CFG_PREDIV_SHIFT 0 +#define CCM_PLL3_CFG_MODE_SEL_SHIFT 15 +#define CCM_PLL3_CFG_MODE_SEL_FRACT (0 << CCM_PLL3_CFG_MODE_SEL_SHIFT) +#define CCM_PLL3_CFG_MODE_SEL_INT (1 << CCM_PLL3_CFG_MODE_SEL_SHIFT) +#define CCM_PLL3_CFG_FUNC_SET_SHIFT 14 +#define CCM_PLL3_CFG_FUNC_SET_270MHZ (0 << CCM_PLL3_CFG_FUNC_SET_SHIFT) +#define CCM_PLL3_CFG_FUNC_SET_297MHZ (1 << CCM_PLL3_CFG_FUNC_SET_SHIFT) +#define CCM_PLL3_CFG_FACTOR_M 0x7f + +#define CCM_PLL5_CFG_OUT_EXT_DIV_P 0x30000 +#define CCM_PLL5_CFG_OUT_EXT_DIV_P_SHIFT 16 + #define CCM_PLL6_CFG_SATA_CLKEN (1U << 14) #define CCM_SD_CLK_SRC_SEL 0x3000000 @@ -160,6 +176,49 @@ #define CCM_AUDIO_CODEC_ENABLE (1U << 31) +#define CCM_LCD_CH0_SCLK_GATING (1U << 31) +#define CCM_LCD_CH0_RESET (1U << 30) +#define CCM_LCD_CH0_SRC_SEL 0x03000000 +#define CCM_LCD_CH0_SRC_SEL_SHIFT 24 +#define CCM_LCD_CH0_SRC_SEL_PLL3 0 +#define CCM_LCD_CH0_SRC_SEL_PLL7 1 +#define CCM_LCD_CH0_SRC_SEL_PLL3_2X 2 +#define CCM_LCD_CH0_SRC_SEL_PLL6_2X 3 + +#define CCM_LCD_CH1_SCLK2_GATING (1U << 31) +#define CCM_LCD_CH1_SRC_SEL 0x03000000 +#define CCM_LCD_CH1_SRC_SEL_SHIFT 24 +#define CCM_LCD_CH1_SRC_SEL_PLL3 0 +#define CCM_LCD_CH1_SRC_SEL_PLL7 1 +#define CCM_LCD_CH1_SRC_SEL_PLL3_2X 2 +#define CCM_LCD_CH1_SRC_SEL_PLL7_2X 3 +#define CCM_LCD_CH1_SCLK1_GATING (1U << 15) +#define CCM_LCD_CH1_SCLK1_SRC_SEL_SHIFT 11 +#define CCM_LCD_CH1_SCLK1_SRC_SEL_SCLK2 0 +#define CCM_LCD_CH1_SCLK1_SRC_SEL_SCLK2_DIV2 1 +#define CCM_LCD_CH1_CLK_DIV_RATIO_M 0xf + +#define CCM_DRAM_CLK_BE1_CLK_ENABLE (1U << 27) +#define CCM_DRAM_CLK_BE0_CLK_ENABLE (1U << 26) + +#define CCM_BE_CLK_SCLK_GATING (1U << 31) +#define CCM_BE_CLK_RESET (1U << 30) +#define CCM_BE_CLK_SRC_SEL 0x03000000 +#define CCM_BE_CLK_SRC_SEL_SHIFT 24 +#define CCM_BE_CLK_SRC_SEL_PLL3 0 +#define CCM_BE_CLK_SRC_SEL_PLL7 1 +#define CCM_BE_CLK_SRC_SEL_PLL5 2 +#define CCM_BE_CLK_DIV_RATIO_M 0xf + +#define CCM_HDMI_CLK_SCLK_GATING (1U << 31) +#define CCM_HDMI_CLK_SRC_SEL 0x03000000 +#define CCM_HDMI_CLK_SRC_SEL_SHIFT 24 +#define CCM_HDMI_CLK_SRC_SEL_PLL3 0 +#define CCM_HDMI_CLK_SRC_SEL_PLL7 1 +#define CCM_HDMI_CLK_SRC_SEL_PLL3_2X 2 +#define CCM_HDMI_CLK_SRC_SEL_PLL7_2X 3 +#define CCM_HDMI_CLK_DIV_RATIO_M 0xf + #define CCM_CLK_REF_FREQ 24000000U int a10_clk_usb_activate(void); @@ -172,5 +231,10 @@ int a10_clk_mmc_cfg(int, int); int a10_clk_i2c_activate(int); int a10_clk_dmac_activate(void); int a10_clk_codec_activate(unsigned int); +int a10_clk_debe_activate(void); +int a10_clk_lcd_activate(void); +int a10_clk_tcon_activate(unsigned int); +int a10_clk_tcon_get_config(int *, int *); +int a10_clk_hdmi_activate(void); #endif /* _A10_CLK_H_ */ Added: head/sys/arm/allwinner/a10_fb.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm/allwinner/a10_fb.c Thu Feb 25 20:17:18 2016 (r296064) @@ -0,0 +1,545 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 ``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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/fbio.h> +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> +#include <vm/pmap.h> + +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/videomode/videomode.h> +#include <dev/videomode/edidvar.h> + +#include <arm/allwinner/a10_clk.h> + +#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 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(kernel_arena, 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(kernel_arena, sc->vaddr, sc->fbsize); +} + +static void +a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) +{ + int width, height, interlace, reg; + uint32_t val; + + interlace = !!(mode->flags & VID_INTERLACE); + width = mode->hdisplay; + height = mode->vdisplay << interlace; + + /* Enable DEBE clocks */ + a10_clk_debe_activate(); + + /* 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); +} + +static void +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; + uint32_t val; + + 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); + + /* Enable LCD clocks */ + a10_clk_lcd_activate(); + + /* 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 */ + a10_clk_tcon_activate(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 */ + a10fb_setup_debe(sc, mode); + + /* Setup display timing controller */ + a10fb_setup_tcon(sc, mode); + + /* 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), +}; + +static devclass_t a10fb_devclass; + +DRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0); Added: head/sys/arm/allwinner/a10_hdmi.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm/allwinner/a10_hdmi.c Thu Feb 25 20:17:18 2016 (r296064) @@ -0,0 +1,601 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 ``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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/videomode/videomode.h> +#include <dev/videomode/edidvar.h> + +#include <arm/allwinner/a10_clk.h> + +#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 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201602252017.u1PKHJcb070363>