[U-Boot] [PATCH] video: add cfb console driver for sunxi

Luc Verhaegen libv at skynet.be
Sat Aug 2 18:14:57 CEST 2014


This adds a fixed mode hdmi driver (lcd to be added in future) for the
sunxi platform. Current config is such that 8MB is shaved off at the top
of the RAM. Simplefb support is available for kernels that know how to
use it.

Signed-off-by: Luc Verhaegen <libv at skynet.be>
---
 arch/arm/include/asm/arch-sunxi/sunxi_display.h |   21 +
 board/sunxi/board.c                             |   14 +
 drivers/video/Makefile                          |    1 +
 drivers/video/sunxi_display.c                   |  639 +++++++++++++++++++++++
 include/configs/sunxi-common.h                  |   34 ++
 5 files changed, 709 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-sunxi/sunxi_display.h
 create mode 100644 drivers/video/sunxi_display.c

diff --git a/arch/arm/include/asm/arch-sunxi/sunxi_display.h b/arch/arm/include/asm/arch-sunxi/sunxi_display.h
new file mode 100644
index 0000000..4456778
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/sunxi_display.h
@@ -0,0 +1,21 @@
+/*
+ * (C) Copyright 2014 Luc Verhaegen <libv at skynet.be>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation's version 2 and any
+ * later version the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _SUNXI_DISPLAY_H_
+#define _SUNXI_DISPLAY_H_
+
+#ifdef CONFIG_VIDEO_DT_SIMPLEFB
+void sunxi_simplefb_setup(void *blob);
+#endif
+
+#endif /* _SUNXI_DISPLAY_H_ */
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 2179e23..e819b12 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -26,6 +26,10 @@
 #include <asm/io.h>
 #include <net.h>
 
+#ifdef CONFIG_VIDEO
+#include <asm/arch-sunxi/sunxi_display.h>
+#endif
+
 DECLARE_GLOBAL_DATA_PTR;
 
 /* add board specific code here */
@@ -185,3 +189,13 @@ int misc_init_r(void)
 	return 0;
 }
 #endif
+
+#ifdef CONFIG_OF_BOARD_SETUP
+void
+ft_board_setup(void *blob, bd_t *bd)
+{
+#ifdef CONFIG_VIDEO_DT_SIMPLEFB
+	sunxi_simplefb_setup(blob);
+#endif
+}
+#endif /* CONFIG_OF_BOARD_SETUP */
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 945f35d..9a25c84 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
 obj-$(CONFIG_VIDEO_SED13806) += sed13806.o
 obj-$(CONFIG_VIDEO_SM501) += sm501.o
 obj-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o
+obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o
 obj-$(CONFIG_VIDEO_TEGRA) += tegra.o
 obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
 obj-$(CONFIG_FORMIKE) += formike.o
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c
new file mode 100644
index 0000000..251fb67
--- /dev/null
+++ b/drivers/video/sunxi_display.c
@@ -0,0 +1,639 @@
+/*
+ * (C) Copyright 2013-2014 Luc Verhaegen <libv at skynet.be>
+ *
+ * Display driver for Allwinner SoCs.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation's version 2 and any
+ * later version the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * This driver does nothing but HDMI at a fixed mode right now. At some
+ * point in the near future, LCD and VGA will be added.
+ *
+ * The display driver infrastructure in uboot does not immediately allow for
+ * modeline creation off of edid. The mode is therefor hardcoded to
+ * 1024x768 at 60Hz 32bpp. This is acceptable for most HDMI monitors, but far
+ * from ideal. If so desired, alter the modeline in video_hw_init()
+ */
+
+#include <common.h>
+
+#include <asm/io.h>
+#include <asm/global_data.h>
+#include <video_fb.h>
+#include <linux/fb.h>
+#include <asm/arch-sunxi/sunxi_display.h>
+
+/* for simplefb */
+#ifdef CONFIG_OF_BOARD_SETUP
+#include <libfdt.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_display {
+	GraphicDevice graphic_device[1];
+	int enabled;
+} sunxi_display[1];
+
+/*
+ * Convenience functions to ease readability, and to provide an easy
+ * comparison with the sunxi kms driver.
+ */
+static unsigned int
+sunxi_io_read(void *base, int offset)
+{
+	return readl(base + offset);
+}
+
+static void
+sunxi_io_write(void *base, int offset, unsigned int value)
+{
+	writel(value, base + offset);
+}
+
+static void
+sunxi_io_mask(void *base, int offset, unsigned int value, unsigned int mask)
+{
+	unsigned int tmp = readl(base + offset);
+
+	tmp &= ~mask;
+	tmp |= value & mask;
+
+	writel(tmp, base + offset);
+}
+
+/*
+ * CCMU regs: clocks.
+ */
+#define SUNXI_CCMU_PLL3_CFG		0x010
+#define SUNXI_CCMU_PLL5_CFG		0x020
+#define SUNXI_CCMU_PLL7_CFG		0x030
+#define SUNXI_CCMU_AHB_GATING1		0x064
+#define SUNXI_CCMU_DRAM_CLK_GATING	0x100
+#define SUNXI_DE_BE0_CLK		0x104
+#define SUNXI_LCDC0_CH0_CLK		0x118
+#define SUNXI_LCDC0_CH1_CLK		0x12C
+#define SUNXI_CCMU_HDMI_CLK		0x150
+
+/*
+ * DEBE regs.
+ *
+ * This is the entity that mixes and matches the different layers and inputs.
+ * Allwinner calls it the back-end, but i like composer better.
+ */
+#define SUNXI_COMP_MODE			0x800
+#define SUNXI_COMP_DISP_SIZE		0x808
+#define SUNXI_COMP_LAYER0_SIZE		0x810
+#define SUNXI_COMP_LAYER0_POS		0x820
+#define SUNXI_COMP_LAYER0_STRIDE	0x840
+#define SUNXI_COMP_LAYER0_ADDR_LOW	0X850
+#define SUNXI_COMP_LAYER_ADDR_HIGH	0X860
+#define SUNXI_COMP_REG_CTL		0X870
+#define SUNXI_COMP_LAYER0_ATTR0		0x890
+#define SUNXI_COMP_LAYER0_ATTR1		0x8a0
+
+/*
+ * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
+ */
+#define SUNXI_LCDC_ENABLE		0x000
+#define SUNXI_LCDC_INT0			0x004
+#define SUNXI_LCDC_INT1			0x008
+#define SUNXI_LCDC_TCON0_DOTCLOCK	0x044
+#define SUNXI_LCDC_TCON0_IO_TRI		0x08c
+#define SUNXI_LCDC_TCON1_ENABLE		0x090
+#define SUNXI_LCDC_TCON1_TIMING_SRC	0x094
+#define SUNXI_LCDC_TCON1_TIMING_SCALE	0x098
+#define SUNXI_LCDC_TCON1_TIMING_OUT	0x09c
+#define SUNXI_LCDC_TCON1_TIMING_H	0x0a0
+#define SUNXI_LCDC_TCON1_TIMING_V	0x0a4
+#define SUNXI_LCDC_TCON1_TIMING_SYNC	0x0a8
+#define SUNXI_LCDC_TCON1_IO_TRI		0x0f4
+
+/*
+ * HDMI regs.
+ */
+#define SUNXI_HDMI_CTRL			0x004
+#define SUNXI_HDMI_INT_CTRL		0x008
+#define SUNXI_HDMI_HPD			0x00c
+#define SUNXI_HDMI_VIDEO_CTRL		0x010
+#define SUNXI_HDMI_VIDEO_SIZE		0x014
+#define SUNXI_HDMI_VIDEO_BP		0x018
+#define SUNXI_HDMI_VIDEO_FP		0x01c
+#define SUNXI_HDMI_VIDEO_SPW		0x020
+#define SUNXI_HDMI_VIDEO_POLARITY	0x024
+#define SUNXI_HDMI_TX_DRIVER0		0x200
+#define SUNXI_HDMI_TX_DRIVER1		0x204
+#define SUNXI_HDMI_TX_DRIVER2		0x208
+#define SUNXI_HDMI_TX_DRIVER3		0x20C
+
+static int
+sunxi_hdmi_hpd_detect(void)
+{
+	void *ccmu = (void *) SUNXI_CCM_BASE;
+	void *hdmi = (void *) SUNXI_HDMI_BASE;
+
+	/* set video pll1 to 300MHz */
+	sunxi_io_write(ccmu, SUNXI_CCMU_PLL7_CFG, 0x8010D064);
+
+	/* Set hdmi parent to video pll1 */
+	sunxi_io_mask(ccmu, SUNXI_CCMU_HDMI_CLK, 0x01000000, 0x03000000);
+
+	/* set ahb gating to pass */
+	sunxi_io_mask(ccmu, SUNXI_CCMU_AHB_GATING1, 0x800, 0x800);
+
+	/* clk on */
+	sunxi_io_mask(ccmu, SUNXI_CCMU_HDMI_CLK, 0x80000000, 0x80000000);
+
+	sunxi_io_write(hdmi, SUNXI_HDMI_CTRL, 0x80000000);
+	sunxi_io_write(hdmi, SUNXI_HDMI_TX_DRIVER0, 0xA0800000);
+
+	udelay(100);
+
+	if (sunxi_io_read(hdmi, SUNXI_HDMI_HPD) & 0x01)
+		return 1;
+
+	/* no need to keep these running. */
+	sunxi_io_write(hdmi, SUNXI_HDMI_CTRL, 0);
+	sunxi_io_mask(ccmu, SUNXI_CCMU_HDMI_CLK, 0, 0x80000000);
+	sunxi_io_mask(ccmu, SUNXI_CCMU_AHB_GATING1, 0, 0x800);
+	sunxi_io_mask(ccmu, SUNXI_CCMU_PLL7_CFG, 0, 0x80000000);
+
+	return 0;
+}
+
+static int
+sunxi_pll5_frequency(void)
+{
+	void *ccmu = (void *) SUNXI_CCM_BASE;
+	unsigned int pll5 = sunxi_io_read(ccmu, SUNXI_CCMU_PLL5_CFG);
+	int n, k, p;
+
+	n = (pll5 >> 8) & 0x1F;
+	k = ((pll5 >> 4) & 0x03) + 1;
+	p = (pll5 >> 16) & 0x03;
+
+	return (24000 * n * k) >> p;
+}
+
+static void
+sunxi_composer_init(void)
+{
+	void *ccmu = (void *) SUNXI_CCM_BASE;
+	void *composer = (void *) SUNXI_DE_BE0_BASE;
+	int pll5 = sunxi_pll5_frequency();
+	int halve;
+
+	if (pll5 < 300000)
+		halve = 0;
+	else
+		halve = 1;
+
+	/* reset off */
+	sunxi_io_mask(ccmu, SUNXI_DE_BE0_CLK, 0x40000000, 0x40000000);
+
+	/* set to pll5 */
+	sunxi_io_mask(ccmu, SUNXI_DE_BE0_CLK, 0x02000000, 0x03000000);
+
+	if (halve)
+		sunxi_io_mask(ccmu, SUNXI_DE_BE0_CLK, 0x01, 0x03);
+	else
+		sunxi_io_mask(ccmu, SUNXI_DE_BE0_CLK, 0, 0x03);
+
+	sunxi_io_mask(ccmu, SUNXI_CCMU_AHB_GATING1, 0x1000, 0x1000);
+	sunxi_io_mask(ccmu, SUNXI_CCMU_DRAM_CLK_GATING,
+		      0x04000000, 0x04000000);
+
+	/* enable */
+	sunxi_io_mask(ccmu, SUNXI_DE_BE0_CLK, 0x80000000, 0x80000000);
+
+	/* engine bug, clear registers after reset. */
+	{
+		/*
+		 * Since uboot prototypes but never declares memset_io, we
+		 * have to do this by hand.
+		 */
+		int i;
+
+		for (i = 0x0800; i < 0x1000; i += 4)
+			sunxi_io_write(composer, i, 0);
+	}
+
+	sunxi_io_mask(composer, SUNXI_COMP_MODE, 0x01, 0x01);
+}
+
+static void
+sunxi_composer_mode_set(struct fb_videomode *mode, unsigned int address)
+{
+	void *composer = (void *) SUNXI_DE_BE0_BASE;
+#define SUNXI_FORMAT_XRGB8888 0x09
+	unsigned int format = SUNXI_FORMAT_XRGB8888;
+
+	/* enable */
+	sunxi_io_write(composer, SUNXI_COMP_DISP_SIZE,
+		       ((mode->yres - 1) << 16) | (mode->xres - 1));
+
+	sunxi_io_write(composer, SUNXI_COMP_LAYER0_SIZE,
+		       ((mode->yres - 1) << 16) | (mode->xres - 1));
+	sunxi_io_write(composer, SUNXI_COMP_LAYER0_STRIDE, mode->xres << 5);
+	sunxi_io_write(composer, SUNXI_COMP_LAYER0_ADDR_LOW, address << 3);
+	sunxi_io_mask(composer, SUNXI_COMP_LAYER_ADDR_HIGH,
+		      address >> 29, 0xFF);
+
+	sunxi_io_mask(composer, SUNXI_COMP_LAYER0_ATTR1, format << 8, 0x0F00);
+
+	sunxi_io_mask(composer, SUNXI_COMP_LAYER0_ATTR1, 0, 0x04);
+	sunxi_io_mask(composer, SUNXI_COMP_LAYER0_ATTR1, 0, 0x03);
+
+	sunxi_io_mask(composer, SUNXI_COMP_MODE, 0x100, 0x100);
+}
+
+static void
+sunxi_lcdc_pll_set(int dotclock)
+{
+	void *ccmu = (void *) SUNXI_CCM_BASE;
+	int value, n, m, diff;
+	int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
+	int best_double = 0;
+
+	if ((dotclock < 20000) || (dotclock > 400000)) {
+		printf("%s: Error: dotclock %d is out of range.\n",
+		       __func__, dotclock);
+		return;
+	}
+
+	for (m = 16; m > 0; m--) {
+		n = (m * dotclock) / 3000;
+
+		if ((n > 9) && (n < 128)) {
+			value = (3000 * n) / m;
+			diff = value - dotclock;
+			if (diff < 0)
+				diff = -diff;
+
+			if (diff < best_diff) {
+				best_diff = diff;
+				best_m = m;
+				best_n = n;
+				best_double = 0;
+			}
+		}
+
+		n++;
+		if ((n > 9) && (n < 128)) {
+			value = (3000 * n) / m;
+			diff = abs(value - dotclock);
+			if (diff < 0)
+				diff = -diff;
+
+			if (diff < best_diff) {
+				best_diff = diff;
+				best_m = m;
+				best_n = n;
+				best_double = 0;
+			}
+		}
+
+		/* these are just duplicates. */
+		if (!(m & 1))
+			continue;
+
+		n = (m * dotclock) / 6000;
+		if ((n > 63) && (n < 128)) {
+			value = (6000 * n) / m;
+			diff = abs(value - dotclock);
+			if (diff < 0)
+				diff = -diff;
+
+			if (diff < best_diff) {
+				best_diff = diff;
+				best_m = m;
+				best_n = n;
+				best_double = 1;
+			}
+		}
+
+		n++;
+		if ((n > 63) && (n < 128)) {
+			value = (6000 * n) / m;
+			diff = abs(value - dotclock);
+			if (diff < 0)
+				diff = -diff;
+
+			if (diff < best_diff) {
+				best_diff = diff;
+				best_m = m;
+				best_n = n;
+				best_double = 1;
+			}
+		}
+	}
+
+#if 0
+	if (best_double)
+		printf("dotclock: %06dkHz = %06dkHz: (2 * 3MHz * %d) / %d\n",
+		       dotclock, (6000 * best_n) / best_m, best_n, best_m);
+	else
+		printf("dotclock: %06dkHz = %06dkHz: (3MHz * %d) / %d\n",
+		       dotclock, (3000 * best_n) / best_m, best_n, best_m);
+#endif
+
+	sunxi_io_mask(ccmu, SUNXI_CCMU_PLL3_CFG, 0x80000000, 0x80000000);
+	sunxi_io_mask(ccmu, SUNXI_CCMU_PLL3_CFG, 0x8000, 0x8000);
+	sunxi_io_mask(ccmu, SUNXI_CCMU_PLL3_CFG, best_n, 0x7F);
+
+	if (best_double)
+		sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK,
+			      0x02000000, 0x03000000);
+	else
+		sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK,
+			      0, 0x03000000);
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, best_m - 1, 0x0F);
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0, 0x0800);
+
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0x80008000, 0x80008000);
+}
+
+static void
+sunxi_lcdc_init(void)
+{
+	void *ccmu = (void *) SUNXI_CCM_BASE;
+	void *lcdc = (void *) SUNXI_LCD0_BASE;
+
+	/* Pll1 was already enabled in hpd detect. */
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH0_CLK, 0x01000000, 0x03000000);
+
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0x01000000, 0x03000000);
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0, 0x0800);
+
+	/* just randomly set it at 30MHz */
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0x09, 0x0F);
+
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH0_CLK, 0x40000000, 0x40000000);
+
+	sunxi_io_mask(ccmu, SUNXI_CCMU_AHB_GATING1, 0x10, 0x10);
+
+	/* toggle ch0 clock */
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH0_CLK, 0x80000000, 0x80000000);
+	while (sunxi_io_read(ccmu, SUNXI_LCDC0_CH0_CLK) & 0x80000000)
+		sunxi_io_mask(ccmu, SUNXI_LCDC0_CH0_CLK, 0, 0x80000000);
+
+	/* toggle ch1 s1 & s2 clocks */
+	sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0x80008000, 0x80008000);
+	while (sunxi_io_read(ccmu, SUNXI_LCDC0_CH1_CLK) & 0x80008000)
+		sunxi_io_mask(ccmu, SUNXI_LCDC0_CH1_CLK, 0, 0x80008000);
+
+	sunxi_io_write(lcdc, SUNXI_LCDC_ENABLE, 0);
+
+	sunxi_io_write(lcdc, SUNXI_LCDC_INT0, 0);
+	sunxi_io_write(lcdc, SUNXI_LCDC_INT1, 0x20);
+
+	/*
+	 * disable tcon0 dot clock:
+	 * This doesn't disable the dotclock, it just nulls the divider.
+	 */
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON0_DOTCLOCK, 0xF0000000);
+
+	/* all io lines disabled. */
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON0_IO_TRI, 0x0FFFFFFF);
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_IO_TRI, 0x0FFFFFFF);
+}
+
+static void
+sunxi_lcdc_mode_set(struct fb_videomode *mode)
+{
+	void *lcdc = (void *) SUNXI_LCD0_BASE;
+	int total;
+
+	/* use tcon1 */
+	sunxi_io_mask(lcdc, SUNXI_LCDC_ENABLE, 0x01, 0x01);
+
+	/* enabled, 0x1E start delay */
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_ENABLE, 0x800001E0);
+
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_TIMING_SRC,
+		       ((mode->xres - 1) << 16) | (mode->yres - 1));
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_TIMING_SCALE,
+		       ((mode->xres - 1) << 16) | (mode->yres - 1));
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_TIMING_OUT,
+		       ((mode->xres - 1) << 16) | (mode->yres - 1));
+
+	total = mode->left_margin + mode->xres + mode->right_margin +
+		mode->hsync_len;
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_TIMING_H,
+		       ((total - 1) << 16) |
+		       (mode->hsync_len + mode->left_margin - 1));
+
+	total = mode->upper_margin + mode->yres + mode->lower_margin +
+		mode->vsync_len;
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_TIMING_V,
+		       ((total *  2) << 16) |
+		       (mode->vsync_len + mode->upper_margin - 1));
+
+	sunxi_io_write(lcdc, SUNXI_LCDC_TCON1_TIMING_SYNC,
+		       ((mode->hsync_len - 1) << 16) | (mode->vsync_len - 1));
+
+	sunxi_lcdc_pll_set(mode->pixclock);
+}
+
+static void
+sunxi_hdmi_mode_set(struct fb_videomode *mode)
+{
+	void *ccmu = (void *) SUNXI_CCM_BASE;
+	void *hdmi = (void *) SUNXI_HDMI_BASE;
+	int h, v, tmp;
+
+	sunxi_io_write(hdmi, SUNXI_HDMI_INT_CTRL, 0xFFFFFFFF);
+	sunxi_io_write(hdmi, SUNXI_HDMI_VIDEO_POLARITY, 0x03FE0000);
+	sunxi_io_mask(hdmi, SUNXI_HDMI_TX_DRIVER0, 0xDE000000, 0xDE000000);
+	sunxi_io_write(hdmi, SUNXI_HDMI_TX_DRIVER1, 0x00D8C820);
+	sunxi_io_write(hdmi, SUNXI_HDMI_TX_DRIVER2, 0xD2000008);
+	sunxi_io_write(hdmi, SUNXI_HDMI_TX_DRIVER3, 0);
+
+	/* use video0 */
+	sunxi_io_mask(hdmi, SUNXI_HDMI_TX_DRIVER3, 0, 0x00200000);
+
+	tmp = sunxi_io_read(ccmu, SUNXI_LCDC0_CH1_CLK);
+	sunxi_io_mask(hdmi, SUNXI_HDMI_TX_DRIVER2,
+		      ((tmp & 0x0F) + 1) << 4, 0xF0);
+
+	if (tmp & 0x02000000)
+		sunxi_io_mask(hdmi, SUNXI_HDMI_TX_DRIVER1, 0, 0x40);
+	else
+		sunxi_io_mask(hdmi, SUNXI_HDMI_TX_DRIVER1, 0x40, 0x40);
+
+	sunxi_io_write(hdmi, SUNXI_HDMI_VIDEO_SIZE,
+		       ((mode->yres - 1) << 16) | (mode->xres - 1));
+
+	h = mode->hsync_len + mode->left_margin;
+	v = mode->vsync_len + mode->upper_margin;
+	sunxi_io_write(hdmi, SUNXI_HDMI_VIDEO_BP, ((v - 1) << 16) | (h - 1));
+
+	h = mode->right_margin;
+	v = mode->lower_margin;
+	sunxi_io_write(hdmi, SUNXI_HDMI_VIDEO_FP, ((v - 1) << 16) | (h - 1));
+
+	h = mode->hsync_len;
+	v = mode->vsync_len;
+	sunxi_io_write(hdmi, SUNXI_HDMI_VIDEO_SPW, ((v - 1) << 16) | (h - 1));
+
+	if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+		sunxi_io_mask(hdmi, SUNXI_HDMI_VIDEO_POLARITY, 0x01, 0x01);
+	else
+		sunxi_io_mask(hdmi, SUNXI_HDMI_VIDEO_POLARITY, 0, 0x01);
+
+	if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+		sunxi_io_mask(hdmi, SUNXI_HDMI_VIDEO_POLARITY, 0x02, 0x02);
+	else
+		sunxi_io_mask(hdmi, SUNXI_HDMI_VIDEO_POLARITY, 0, 0x02);
+}
+
+static void
+sunxi_engines_init(void)
+{
+	sunxi_composer_init();
+	sunxi_lcdc_init();
+}
+
+static void
+sunxi_mode_set(struct fb_videomode *mode, unsigned int address)
+{
+	void *composer = (void *) SUNXI_DE_BE0_BASE;
+	void *lcdc = (void *) SUNXI_LCD0_BASE;
+	void *hdmi = (void *) SUNXI_HDMI_BASE;
+
+	sunxi_io_mask(hdmi, SUNXI_HDMI_VIDEO_CTRL, 0, 0x80000000);
+	sunxi_io_mask(lcdc, SUNXI_LCDC_ENABLE, 0, 0x80000000);
+	sunxi_io_mask(composer, SUNXI_COMP_MODE, 0, 0x02);
+
+	sunxi_composer_mode_set(mode, address);
+	sunxi_lcdc_mode_set(mode);
+	sunxi_hdmi_mode_set(mode);
+
+	sunxi_io_mask(composer, SUNXI_COMP_REG_CTL, 0x01, 0x01);
+	sunxi_io_mask(composer, SUNXI_COMP_MODE, 0x02, 0x02);
+
+	sunxi_io_mask(lcdc, SUNXI_LCDC_ENABLE, 0x80000000, 0x80000000);
+	sunxi_io_mask(lcdc, SUNXI_LCDC_TCON1_IO_TRI, 0x00000000, 0x03000000);
+
+	udelay(100);
+
+	sunxi_io_mask(hdmi, SUNXI_HDMI_VIDEO_CTRL, 0x80000000, 0x80000000);
+}
+
+#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
+void
+sunxi_simplefb_setup(void *blob)
+{
+	static GraphicDevice *graphic_device = sunxi_display->graphic_device;
+	const char *name = "simple-framebuffer";
+	const char *format = "x8r8g8b8";
+	fdt32_t cells[2];
+	int offset, stride, ret;
+
+	if (!sunxi_display->enabled)
+		return;
+
+	offset = fdt_add_subnode(blob, 0, "framebuffer");
+	if (offset < 0) {
+		printf("%s: add subnode failed", __func__);
+		return;
+	}
+
+	ret = fdt_setprop(blob, offset, "compatible", name, strlen(name) + 1);
+	if (ret < 0)
+		return;
+
+	stride = graphic_device->winSizeX * graphic_device->gdfBytesPP;
+
+	cells[0] = cpu_to_fdt32(gd->fb_base);
+	cells[1] = cpu_to_fdt32(CONFIG_SUNXI_FB_SIZE);
+	ret = fdt_setprop(blob, offset, "reg", cells, sizeof(cells[0]) * 2);
+	if (ret < 0)
+		return;
+
+	cells[0] = cpu_to_fdt32(graphic_device->winSizeX);
+	ret = fdt_setprop(blob, offset, "width", cells, sizeof(cells[0]));
+	if (ret < 0)
+		return;
+
+	cells[0] = cpu_to_fdt32(graphic_device->winSizeY);
+	ret = fdt_setprop(blob, offset, "height", cells, sizeof(cells[0]));
+	if (ret < 0)
+		return;
+
+	cells[0] = cpu_to_fdt32(stride);
+	ret = fdt_setprop(blob, offset, "stride", cells, sizeof(cells[0]));
+	if (ret < 0)
+		return;
+
+	ret = fdt_setprop(blob, offset, "format", format, strlen(format) + 1);
+	if (ret < 0)
+		return;
+}
+#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */
+
+void *
+video_hw_init(void)
+{
+	static GraphicDevice *graphic_device = sunxi_display->graphic_device;
+	/*
+	 * Vesa standard 1024x768 at 60
+	 * 65.0  1024 1032 1176 1344  768 771 777 806  -hsync -vsync
+	 */
+	struct fb_videomode mode = {
+		.name = "1024x768",
+		.refresh = 60,
+		.xres = 1024,
+		.yres = 768,
+		.pixclock = 65000,
+		.left_margin = 160,
+		.right_margin = 24,
+		.upper_margin = 29,
+		.lower_margin = 3,
+		.hsync_len = 136,
+		.vsync_len = 6,
+		.sync = 0,
+		.vmode = 0,
+		.flag = 0,
+	};
+	int ret;
+
+	memset(sunxi_display, 0, sizeof(struct sunxi_display));
+
+	printf("Reserved %dkB of RAM for Framebuffer.\n",
+	       CONFIG_SUNXI_FB_SIZE >> 10);
+	gd->fb_base = gd->ram_top;
+
+	ret = sunxi_hdmi_hpd_detect();
+	if (!ret)
+		return NULL;
+
+	printf("HDMI connected.\n");
+	sunxi_display->enabled = 1;
+
+	printf("Setting up a %s console.\n", mode.name);
+	sunxi_engines_init();
+	sunxi_mode_set(&mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
+
+	/*
+	 * These are the only members of this structure that are used. All the
+	 * others are driver specific. There is nothing to decribe pitch or
+	 * stride, but we are lucky with our hw.
+	 */
+	graphic_device->frameAdrs = gd->fb_base;
+	graphic_device->gdfIndex = GDF_32BIT_X888RGB;
+	graphic_device->gdfBytesPP = 4;
+	graphic_device->winSizeX = mode.xres;
+	graphic_device->winSizeY = mode.yres;
+
+	return graphic_device;
+}
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index 8ab6429..89582b8 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -181,6 +181,40 @@
 #define CONFIG_SUNXI_GPIO
 #define CONFIG_CMD_GPIO
 
+/* Set this if you do not want a uboot fb console */
+#ifndef CONFIG_NO_VIDEO
+#define CONFIG_VIDEO
+#endif
+
+#ifdef CONFIG_VIDEO
+/*
+ * The amount of RAM that is reserved for the FB. This will not show up as
+ * RAM to the kernel, but will be reclaimed by a KMS driver in future.
+ */
+#define CONFIG_SUNXI_FB_SIZE (8 << 20)
+
+/* Do we want to initialize a simple FB? */
+#define CONFIG_VIDEO_DT_SIMPLEFB
+
+#define CONFIG_VIDEO_SUNXI
+
+#define CONFIG_CFB_CONSOLE
+/* allow both serial and cfb console. */
+#define CONFIG_CONSOLE_MUX
+/* this is needed so the above will actually do something */
+#define CONFIG_SYS_CONSOLE_IS_IN_ENV
+/* stop x86 thinking in cfbconsole from trying to init a pc keyboard */
+#define CONFIG_VGA_AS_SINGLE_DEVICE
+
+#define CONFIG_SYS_MEM_TOP_HIDE ((CONFIG_SUNXI_FB_SIZE + 0xFFF) & ~0xFFF)
+
+/* To be able to hook simplefb into dt */
+#ifdef CONFIG_VIDEO_DT_SIMPLEFB
+#define CONFIG_OF_BOARD_SETUP
+#endif
+
+#endif /* CONFIG_VIDEO */
+
 /* Ethernet support */
 #ifdef CONFIG_SUNXI_EMAC
 #define CONFIG_MII			/* MII PHY management		*/
-- 
1.7.7



More information about the U-Boot mailing list