[U-Boot] [PATCH] video: add cfb console driver for sunxi
Hans de Goede
hdegoede at redhat.com
Mon Aug 4 10:39:13 CEST 2014
Hi Luc,
On 08/02/2014 06:14 PM, Luc Verhaegen wrote:
> 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>
First of all many thanks for your work on this.
ATM I don't have time to do a full review, but I don't expect there
to be too many suprises when I do find the time.
Really my only concern is the handover of the reserved memory, etc. to
the kernel. We need to get a plan in place for that *before* this can
be merged. Note I don't want to raise any artificial barriers here,
I would love to see this merged ASAP. But I don't want to paint us
in a corner where u-boot having hdmi console support makes it harder
to get kms support in the kernel going. I think we can both agree on that.
So I really want to see some plan how this will work in place before merging.
Note just a plan, I don't expect kernel patches ready to be merged for this,
just a good idea / sketch of how all the bits will fit together.
In one of the threads about this there was some discussion about doing a
"flicker free" handover. I agree with you that given that we will be fixed
to 1024x768 in u-boot this won't be realistic. But in the light of that
it would be nice if we could make it so that if none of the stdout and stderr
variables point to vga we don't init the hdmi at all, this will avoid
what ever is attached to first have to sync at 1024x768 and then at its
native resolution when the kernel takes over, and this will also allow
for an easy way to not steal the 8MB of memory for people who care about
that (think headless server which is low on memory)
Regards,
Hans
> ---
> 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 */
>
More information about the U-Boot
mailing list