Index: head/sys/arm/allwinner/a10_clk.c =================================================================== --- head/sys/arm/allwinner/a10_clk.c (revision 296063) +++ head/sys/arm/allwinner/a10_clk.c (revision 296064) @@ -1,467 +1,707 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* Simple clock driver for Allwinner A10 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #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; bus_space_handle_t bsh; int pll6_enabled; }; static struct a10_ccm_softc *a10_ccm_sc = NULL; #define ccm_read_4(sc, reg) \ bus_space_read_4((sc)->bst, (sc)->bsh, (reg)) #define ccm_write_4(sc, reg, val) \ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val)) static int a10_ccm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "allwinner,sun4i-ccm")) { device_set_desc(dev, "Allwinner Clock Control Module"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int a10_ccm_attach(device_t dev) { struct a10_ccm_softc *sc = device_get_softc(dev); int rid = 0; if (a10_ccm_sc) return (ENXIO); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->res) { device_printf(dev, "could not allocate resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); a10_ccm_sc = sc; return (0); } static device_method_t a10_ccm_methods[] = { DEVMETHOD(device_probe, a10_ccm_probe), DEVMETHOD(device_attach, a10_ccm_attach), { 0, 0 } }; static driver_t a10_ccm_driver = { "a10_ccm", a10_ccm_methods, sizeof(struct a10_ccm_softc), }; static devclass_t a10_ccm_devclass; EARLY_DRIVER_MODULE(a10_ccm, simplebus, a10_ccm_driver, a10_ccm_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); int a10_clk_usb_activate(void) { struct a10_ccm_softc *sc = a10_ccm_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Gating AHB clock for USB */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_USB0; /* AHB clock gate usb0 */ reg_value |= CCM_AHB_GATING_EHCI0; /* AHB clock gate ehci0 */ reg_value |= CCM_AHB_GATING_EHCI1; /* AHB clock gate ehci1 */ ccm_write_4(sc, CCM_AHB_GATING0, reg_value); /* Enable clock for USB */ reg_value = ccm_read_4(sc, CCM_USB_CLK); reg_value |= CCM_USB_PHY; /* USBPHY */ reg_value |= CCM_USB0_RESET; /* disable reset for USB0 */ reg_value |= CCM_USB1_RESET; /* disable reset for USB1 */ reg_value |= CCM_USB2_RESET; /* disable reset for USB2 */ ccm_write_4(sc, CCM_USB_CLK, reg_value); return (0); } int a10_clk_usb_deactivate(void) { struct a10_ccm_softc *sc = a10_ccm_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Disable clock for USB */ reg_value = ccm_read_4(sc, CCM_USB_CLK); reg_value &= ~CCM_USB_PHY; /* USBPHY */ reg_value &= ~CCM_USB0_RESET; /* reset for USB0 */ reg_value &= ~CCM_USB1_RESET; /* reset for USB1 */ reg_value &= ~CCM_USB2_RESET; /* reset for USB2 */ ccm_write_4(sc, CCM_USB_CLK, reg_value); /* Disable gating AHB clock for USB */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value &= ~CCM_AHB_GATING_USB0; /* disable AHB clock gate usb0 */ reg_value &= ~CCM_AHB_GATING_EHCI0; /* disable AHB clock gate ehci0 */ reg_value &= ~CCM_AHB_GATING_EHCI1; /* disable AHB clock gate ehci1 */ ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_emac_activate(void) { struct a10_ccm_softc *sc = a10_ccm_sc; uint32_t reg_value; if (sc == NULL) return (ENXIO); /* Gating AHB clock for EMAC */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_EMAC; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_gmac_activate(phandle_t node) { char *phy_type; struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); /* Gating AHB clock for GMAC */ reg_value = ccm_read_4(sc, CCM_AHB_GATING1); reg_value |= CCM_AHB_GATING_GMAC; ccm_write_4(sc, CCM_AHB_GATING1, reg_value); /* Set GMAC mode. */ reg_value = CCM_GMAC_CLK_MII; if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) > 0) { if (strcasecmp(phy_type, "rgmii") == 0) reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII; else if (strcasecmp(phy_type, "rgmii-bpi") == 0) { reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII; reg_value |= (3 << CCM_GMAC_CLK_DELAY_SHIFT); } free(phy_type, M_OFWPROP); } ccm_write_4(sc, CCM_GMAC_CLK, reg_value); return (0); } static void a10_clk_pll6_enable(void) { struct a10_ccm_softc *sc; uint32_t reg_value; /* * SATA needs PLL6 to be a 100MHz clock. * The SATA output frequency is 24MHz * n * k / m / 6. * To get to 100MHz, k & m must be equal and n must be 25. * For other uses the output frequency is 24MHz * n * k / 2. */ sc = a10_ccm_sc; if (sc->pll6_enabled) return; reg_value = ccm_read_4(sc, CCM_PLL6_CFG); reg_value &= ~CCM_PLL_CFG_BYPASS; reg_value &= ~(CCM_PLL_CFG_FACTOR_K | CCM_PLL_CFG_FACTOR_M | CCM_PLL_CFG_FACTOR_N); reg_value |= (25 << CCM_PLL_CFG_FACTOR_N_SHIFT); reg_value |= CCM_PLL6_CFG_SATA_CLKEN; reg_value |= CCM_PLL_CFG_ENABLE; ccm_write_4(sc, CCM_PLL6_CFG, reg_value); sc->pll6_enabled = 1; } static unsigned int a10_clk_pll6_get_rate(void) { struct a10_ccm_softc *sc; uint32_t k, n, reg_value; sc = a10_ccm_sc; reg_value = ccm_read_4(sc, CCM_PLL6_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; return ((CCM_CLK_REF_FREQ * n * k) / 2); } static int a10_clk_pll2_set_rate(unsigned int freq) { struct a10_ccm_softc *sc; uint32_t reg_value; unsigned int prediv, postdiv, n; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); reg_value = ccm_read_4(sc, CCM_PLL2_CFG); reg_value &= ~(CCM_PLL2_CFG_PREDIV | CCM_PLL2_CFG_POSTDIV | CCM_PLL_CFG_FACTOR_N); /* * Audio Codec needs PLL2 to be either 24576000 Hz or 22579200 Hz * * PLL2 output frequency is 24MHz * n / prediv / postdiv. * To get as close as possible to the desired rate, we use a * pre-divider of 21 and a post-divider of 4. With these values, * a multiplier of 86 or 79 gets us close to the target rates. */ prediv = 21; postdiv = 4; switch (freq) { case 24576000: n = 86; reg_value |= CCM_PLL_CFG_ENABLE; break; case 22579200: n = 79; reg_value |= CCM_PLL_CFG_ENABLE; break; case 0: n = 1; reg_value &= ~CCM_PLL_CFG_ENABLE; break; default: return (EINVAL); } reg_value |= (prediv << CCM_PLL2_CFG_PREDIV_SHIFT); reg_value |= (postdiv << CCM_PLL2_CFG_POSTDIV_SHIFT); reg_value |= (n << CCM_PLL_CFG_FACTOR_N_SHIFT); ccm_write_4(sc, CCM_PLL2_CFG, reg_value); 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) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll6_enable(); /* Gating AHB clock for SATA */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_SATA; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); DELAY(1000); ccm_write_4(sc, CCM_SATA_CLK, CCM_PLL_CFG_ENABLE); return (0); } int a10_clk_mmc_activate(int devid) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll6_enable(); /* Gating AHB clock for SD/MMC */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_SDMMC0 << devid; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_mmc_cfg(int devid, int freq) { struct a10_ccm_softc *sc; uint32_t clksrc, m, n, ophase, phase, reg_value; unsigned int pll_freq; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); freq /= 1000; if (freq <= 400) { pll_freq = CCM_CLK_REF_FREQ / 1000; clksrc = CCM_SD_CLK_SRC_SEL_OSC24M; ophase = 0; phase = 0; n = 2; } else if (freq <= 25000) { pll_freq = a10_clk_pll6_get_rate() / 1000; clksrc = CCM_SD_CLK_SRC_SEL_PLL6; ophase = 0; phase = 5; n = 2; } else if (freq <= 50000) { pll_freq = a10_clk_pll6_get_rate() / 1000; clksrc = CCM_SD_CLK_SRC_SEL_PLL6; ophase = 3; phase = 5; n = 0; } else return (EINVAL); m = ((pll_freq / (1 << n)) / (freq)) - 1; reg_value = ccm_read_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4)); reg_value &= ~CCM_SD_CLK_SRC_SEL; reg_value |= (clksrc << CCM_SD_CLK_SRC_SEL_SHIFT); reg_value &= ~CCM_SD_CLK_PHASE_CTR; reg_value |= (phase << CCM_SD_CLK_PHASE_CTR_SHIFT); reg_value &= ~CCM_SD_CLK_DIV_RATIO_N; reg_value |= (n << CCM_SD_CLK_DIV_RATIO_N_SHIFT); reg_value &= ~CCM_SD_CLK_OPHASE_CTR; reg_value |= (ophase << CCM_SD_CLK_OPHASE_CTR_SHIFT); reg_value &= ~CCM_SD_CLK_DIV_RATIO_M; reg_value |= m; reg_value |= CCM_PLL_CFG_ENABLE; ccm_write_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4), reg_value); return (0); } int a10_clk_i2c_activate(int devid) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll6_enable(); /* Gating APB clock for I2C/TWI */ reg_value = ccm_read_4(sc, CCM_APB1_GATING); if (devid == 4) reg_value |= CCM_APB1_GATING_TWI << 15; else reg_value |= CCM_APB1_GATING_TWI << devid; ccm_write_4(sc, CCM_APB1_GATING, reg_value); return (0); } int a10_clk_dmac_activate(void) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); /* Gating AHB clock for DMA controller */ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); reg_value |= CCM_AHB_GATING_DMA; ccm_write_4(sc, CCM_AHB_GATING0, reg_value); return (0); } int a10_clk_codec_activate(unsigned int freq) { struct a10_ccm_softc *sc; uint32_t reg_value; sc = a10_ccm_sc; if (sc == NULL) return (ENXIO); a10_clk_pll2_set_rate(freq); /* Gating APB clock for ADDA */ reg_value = ccm_read_4(sc, CCM_APB0_GATING); reg_value |= CCM_APB0_GATING_ADDA; ccm_write_4(sc, CCM_APB0_GATING, reg_value); /* Enable audio codec clock */ reg_value = ccm_read_4(sc, CCM_AUDIO_CODEC_CLK); reg_value |= CCM_AUDIO_CODEC_ENABLE; ccm_write_4(sc, CCM_AUDIO_CODEC_CLK, reg_value); + + 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); } Index: head/sys/arm/allwinner/a10_clk.h =================================================================== --- head/sys/arm/allwinner/a10_clk.h (revision 296063) +++ head/sys/arm/allwinner/a10_clk.h (revision 296064) @@ -1,176 +1,240 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _A10_CLK_H_ #define _A10_CLK_H_ #define CCM_PLL1_CFG 0x0000 #define CCM_PLL1_TUN 0x0004 #define CCM_PLL2_CFG 0x0008 #define CCM_PLL2_TUN 0x000c #define CCM_PLL3_CFG 0x0010 #define CCM_PLL3_TUN 0x0014 #define CCM_PLL4_CFG 0x0018 #define CCM_PLL4_TUN 0x001c #define CCM_PLL5_CFG 0x0020 #define CCM_PLL5_TUN 0x0024 #define CCM_PLL6_CFG 0x0028 #define CCM_PLL6_TUN 0x002c #define CCM_PLL7_CFG 0x0030 #define CCM_PLL7_TUN 0x0034 #define CCM_PLL1_TUN2 0x0038 #define CCM_PLL5_TUN2 0x003c #define CCM_PLL_LOCK_DBG 0x004c #define CCM_OSC24M_CFG 0x0050 #define CCM_CPU_AHB_APB0_CFG 0x0054 #define CCM_APB1_CLK_DIV 0x0058 #define CCM_AXI_GATING 0x005c #define CCM_AHB_GATING0 0x0060 #define CCM_AHB_GATING1 0x0064 #define CCM_APB0_GATING 0x0068 #define CCM_APB1_GATING 0x006c #define CCM_NAND_SCLK_CFG 0x0080 #define CCM_MS_SCLK_CFG 0x0084 #define CCM_MMC0_SCLK_CFG 0x0088 #define CCM_MMC1_SCLK_CFG 0x008c #define CCM_MMC2_SCLK_CFG 0x0090 #define CCM_MMC3_SCLK_CFG 0x0094 #define CCM_TS_CLK 0x0098 #define CCM_SS_CLK 0x009c #define CCM_SPI0_CLK 0x00a0 #define CCM_SPI1_CLK 0x00a4 #define CCM_SPI2_CLK 0x00a8 #define CCM_PATA_CLK 0x00ac #define CCM_IR0_CLK 0x00b0 #define CCM_IR1_CLK 0x00b4 #define CCM_IIS_CLK 0x00b8 #define CCM_AC97_CLK 0x00bc #define CCM_SPDIF_CLK 0x00c0 #define CCM_KEYPAD_CLK 0x00c4 #define CCM_SATA_CLK 0x00c8 #define CCM_USB_CLK 0x00cc #define CCM_GPS_CLK 0x00d0 #define CCM_SPI3_CLK 0x00d4 #define CCM_DRAM_CLK 0x0100 #define CCM_BE0_SCLK 0x0104 #define CCM_BE1_SCLK 0x0108 #define CCM_FE0_CLK 0x010c #define CCM_FE1_CLK 0x0110 #define CCM_MP_CLK 0x0114 #define CCM_LCD0_CH0_CLK 0x0118 #define CCM_LCD1_CH0_CLK 0x011c #define CCM_CSI_ISP_CLK 0x0120 #define CCM_TVD_CLK 0x0128 #define CCM_LCD0_CH1_CLK 0x012c #define CCM_LCD1_CH1_CLK 0x0130 #define CCM_CS0_CLK 0x0134 #define CCM_CS1_CLK 0x0138 #define CCM_VE_CLK 0x013c #define CCM_AUDIO_CODEC_CLK 0x0140 #define CCM_AVS_CLK 0x0144 #define CCM_ACE_CLK 0x0148 #define CCM_LVDS_CLK 0x014c #define CCM_HDMI_CLK 0x0150 #define CCM_MALI400_CLK 0x0154 #define CCM_GMAC_CLK 0x0164 #define CCM_GMAC_CLK_DELAY_SHIFT 10 #define CCM_GMAC_CLK_MODE_MASK 0x7 #define CCM_GMAC_MODE_RGMII (1 << 2) #define CCM_GMAC_CLK_MII 0x0 #define CCM_GMAC_CLK_EXT_RGMII 0x1 #define CCM_GMAC_CLK_RGMII 0x2 /* APB0_GATING */ #define CCM_APB0_GATING_ADDA (1 << 0) /* AHB_GATING_REG0 */ #define CCM_AHB_GATING_USB0 (1 << 0) #define CCM_AHB_GATING_EHCI0 (1 << 1) #define CCM_AHB_GATING_EHCI1 (1 << 3) #define CCM_AHB_GATING_DMA (1 << 6) #define CCM_AHB_GATING_SDMMC0 (1 << 8) #define CCM_AHB_GATING_EMAC (1 << 17) #define CCM_AHB_GATING_SATA (1 << 25) /* 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) #define CCM_USB1_RESET (1 << 1) #define CCM_USB2_RESET (1 << 2) #define CCM_PLL_CFG_ENABLE (1U << 31) #define CCM_PLL_CFG_BYPASS (1U << 30) #define CCM_PLL_CFG_PLL5 (1U << 25) #define CCM_PLL_CFG_PLL6 (1U << 24) #define CCM_PLL_CFG_FACTOR_N 0x1f00 #define CCM_PLL_CFG_FACTOR_N_SHIFT 8 #define CCM_PLL_CFG_FACTOR_K 0x30 #define CCM_PLL_CFG_FACTOR_K_SHIFT 4 #define CCM_PLL_CFG_FACTOR_M 0x3 #define CCM_PLL2_CFG_POSTDIV 0x3c000000 #define CCM_PLL2_CFG_POSTDIV_SHIFT 26 #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 #define CCM_SD_CLK_SRC_SEL_SHIFT 24 #define CCM_SD_CLK_SRC_SEL_OSC24M 0 #define CCM_SD_CLK_SRC_SEL_PLL6 1 #define CCM_SD_CLK_PHASE_CTR 0x700000 #define CCM_SD_CLK_PHASE_CTR_SHIFT 20 #define CCM_SD_CLK_DIV_RATIO_N 0x30000 #define CCM_SD_CLK_DIV_RATIO_N_SHIFT 16 #define CCM_SD_CLK_OPHASE_CTR 0x700 #define CCM_SD_CLK_OPHASE_CTR_SHIFT 8 #define CCM_SD_CLK_DIV_RATIO_M 0xf #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); int a10_clk_usb_deactivate(void); int a10_clk_emac_activate(void); int a10_clk_gmac_activate(phandle_t); int a10_clk_ahci_activate(void); int a10_clk_mmc_activate(int); 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_ */ Index: head/sys/arm/allwinner/a10_fb.c =================================================================== --- head/sys/arm/allwinner/a10_fb.c (nonexistent) +++ head/sys/arm/allwinner/a10_fb.c (revision 296064) @@ -0,0 +1,545 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * 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 +__FBSDID("$FreeBSD$"); + +#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 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); Property changes on: head/sys/arm/allwinner/a10_fb.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/allwinner/a10_hdmi.c =================================================================== --- head/sys/arm/allwinner/a10_hdmi.c (nonexistent) +++ head/sys/arm/allwinner/a10_hdmi.c (revision 296064) @@ -0,0 +1,601 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * 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 +__FBSDID("$FreeBSD$"); + +#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 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) + +struct a10hdmi_softc { + struct resource *res; + + struct intr_config_hook mode_hook; + + uint8_t edid[EDID_LENGTH]; + + int has_audio; +}; + +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); + } + + a10_clk_hdmi_activate(); + + 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_audio(struct a10hdmi_softc *sc) +{ + struct edid_info ei; + uint8_t edid[EDID_LENGTH]; + int block; + + if (edid_parse(sc->edid, &ei) != 0) + return (0); + + /* 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) + break; + + if (edid[EXT_TAG] == CEA_TAG_ID) + return ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); + } + + /* No CEA-861 block found */ + return (0); +} + +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); + + /* 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) + sc->has_audio = a10hdmi_detect_audio(sc); + else + 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_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 = a10_clk_tcon_get_config(&clk_div, &clk_dbl); + if (error != 0) + 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 */ + 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), +}; + +static devclass_t a10hdmi_devclass; + +DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0); +MODULE_VERSION(a10hdmi, 1); Property changes on: head/sys/arm/allwinner/a10_hdmi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/allwinner/a10_hdmiaudio.c =================================================================== --- head/sys/arm/allwinner/a10_hdmiaudio.c (nonexistent) +++ head/sys/arm/allwinner/a10_hdmiaudio.c (revision 296064) @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * 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 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, pcm_devclass, 0, 0); +MODULE_DEPEND(a10hdmiaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(a10hdmiaudio, 1); Property changes on: head/sys/arm/allwinner/a10_hdmiaudio.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/allwinner/files.allwinner =================================================================== --- head/sys/arm/allwinner/files.allwinner (revision 296063) +++ head/sys/arm/allwinner/files.allwinner (revision 296064) @@ -1,20 +1,25 @@ # $FreeBSD$ kern/kern_clocksource.c standard arm/allwinner/a10_ahci.c optional ahci arm/allwinner/a10_clk.c standard arm/allwinner/a10_codec.c optional sound arm/allwinner/a10_common.c standard arm/allwinner/a10_dmac.c standard arm/allwinner/a10_ehci.c optional ehci arm/allwinner/a10_gpio.c optional gpio arm/allwinner/a10_mmc.c optional mmc arm/allwinner/a10_sramc.c standard arm/allwinner/aw_wdog.c standard arm/allwinner/a20/a20_cpu_cfg.c standard arm/allwinner/allwinner_machdep.c standard arm/allwinner/axp209.c optional axp209 arm/allwinner/if_emac.c optional emac arm/allwinner/sunxi_dma_if.m standard dev/iicbus/twsi/a10_twsi.c optional twsi #arm/allwinner/console.c standard + +arm/allwinner/a10_fb.c optional vt +arm/allwinner/a10_hdmi.c optional hdmi +arm/allwinner/a10_hdmiaudio.c optional hdmi sound +arm/arm/hdmi_if.m optional hdmi Index: head/sys/arm/arm/hdmi_if.m =================================================================== --- head/sys/arm/arm/hdmi_if.m (revision 296063) +++ head/sys/arm/arm/hdmi_if.m (revision 296064) @@ -1,59 +1,67 @@ #- # Copyright (c) 2015 Oleksandr Tymoshenko # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # #include #include #include INTERFACE hdmi; HEADER { #include typedef void (*hdmi_event_hook)(void *, device_t, int); EVENTHANDLER_DECLARE(hdmi_event, hdmi_event_hook); #define HDMI_EVENT_CONNECTED 0 } # # Get EDID info # METHOD int get_edid { device_t dev; uint8_t **edid; uint32_t *edid_length; }; # # Set videomode # METHOD int set_videomode { device_t dev; const struct videomode *videomode; }; + +# +# Enable/disable output +# +METHOD int enable { + device_t dev; + int onoff; +}; Index: head/sys/arm/conf/A20 =================================================================== --- head/sys/arm/conf/A20 (revision 296063) +++ head/sys/arm/conf/A20 (revision 296064) @@ -1,125 +1,133 @@ # # A20 -- Custom configuration for the Allwinner A20 ARM SoC # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident A20 include "std.armv6" include "../allwinner/a20/std.a20" options ARM_INTRNG options SOC_ALLWINNER_A20 options HZ=100 options SCHED_ULE # ULE scheduler options SMP # Enable multiple cores options PLATFORM # Debugging for use in -current makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols options ALT_BREAK_TO_DEBUGGER #options VERBOSE_SYSINIT # Enable verbose sysinit messages options KDB # Enable kernel debugger support # For minimum debugger support (stable branch) use: #options KDB_TRACE # Print a stack trace for a panic # For full debugger support use this instead: options DDB # Enable the kernel debugger options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed #options DIAGNOSTIC # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=dwc0 # Interrupt controller device gic # ARM Generic Timer device generic_timer # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards # ATA controllers device ahci # AHCI-compatible SATA controllers #device ata # Legacy ATA/SATA controllers # Console and misc device uart device uart_ns8250 device pty device snp device md device random # Entropy device # I2C support device iicbus device iic device twsi device axp209 # AXP209 Power Management Unit # GPIO device gpio device gpioled device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device pass # Passthrough device (direct ATA/SCSI access) # USB support options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. device usb options USB_DEBUG #options USB_REQ_DEBUG #options USB_VERBOSE #device uhci #device ohci device ehci device umass # Ethernet device loop device ether device mii device bpf #device emac # 10/100 integrated EMAC controller device dwc # 10/100/1000 integrated GMAC controller # USB ethernet support, requires miibus device miibus # Sound support device sound # Pinmux device fdt_pinctrl +# Framebuffer support +device vt +device kbdmux +device ums +device ukbd +device videomode +device hdmi + # Flattened Device Tree options FDT # Configure using FDT/DTB data makeoptions MODULES_EXTRA=dtb/allwinner Index: head/sys/boot/fdt/dts/arm/cubieboard2.dts =================================================================== --- head/sys/boot/fdt/dts/arm/cubieboard2.dts (revision 296063) +++ head/sys/boot/fdt/dts/arm/cubieboard2.dts (revision 296064) @@ -1,41 +1,50 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * Copyright (c) 2016 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "sun7i-a20-cubieboard2.dts" +#include "sun7i-a20-hdmi.dtsi" / { soc@01c00000 { ccm@01c20000 { compatible = "allwinner,sun4i-ccm"; #address-cells = <1>; #size-cells = <1>; reg = < 0x01c20000 0x400 >; + }; + + hdmi@01c16000 { + status = "okay"; + }; + + hdmiaudio { + status = "okay"; }; }; }; Index: head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi (nonexistent) +++ head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi (revision 296064) @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/ { + soc@01c00000 { + hdmi: hdmi@01c16000 { + compatible = "allwinner,sun7i-a20-hdmi"; + reg = <0x01c16000 0x1000>; + status = "disabled"; + }; + + hdmiaudio { + compatible = "allwinner,sun7i-a20-hdmiaudio"; + status = "disabled"; + }; + + fb: fb@01e60000 { + compatible = "allwinner,sun7i-a20-fb"; + reg = <0x01e60000 0x10000>, /* DEBE0 */ + <0x01c0c000 0x1000>; /* LCD0 */ + }; + }; +}; Property changes on: head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property