svn commit: r296064 - in head/sys: arm/allwinner arm/arm arm/conf boot/fdt/dts/arm

Jared McNeill jmcneill at FreeBSD.org
Thu Feb 25 20:17:20 UTC 2016


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 at 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 at 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 ***


More information about the svn-src-head mailing list