2169 lines
64 KiB
Diff
2169 lines
64 KiB
Diff
commit ff787b168972bc8747ea152b38d4bc9447ba2fbe
|
|
Author: Jernej Skrabec <jernej.skrabec@siol.net>
|
|
Date: Mon Feb 20 21:54:02 2017 +0100
|
|
|
|
sunxi: video: Add video driver for H3/H5/A64 SoC
|
|
|
|
This patch adds support for hdmi and composite output. It is designed
|
|
in the same way as video driver for older Allwinner SoCs.
|
|
|
|
HDMI: First it checks if monitor is attached. If it is, recommended
|
|
timings are read from EDID. After that, DE2, TCON and HDMI are
|
|
configured according to this timings.
|
|
|
|
Composite output doesn't use any check if monitor is attached. This
|
|
can be added later.
|
|
|
|
32MB of RAM is used for framebuffer. This is just enough to support
|
|
4K resolution.
|
|
|
|
SimpleFB is also supported by this driver.
|
|
|
|
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
|
|
|
diff --git a/u-boot/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/u-boot/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
|
|
index 3f87672..4e93af5 100644
|
|
--- a/u-boot/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
|
|
+++ b/u-boot/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
|
|
@@ -67,14 +67,24 @@ struct sunxi_ccm_reg {
|
|
u32 dram_pll_cfg; /* 0xf8 PLL_DDR cfg register, A33 only */
|
|
u32 mbus_reset; /* 0xfc MBUS reset control, A33 only */
|
|
u32 dram_clk_gate; /* 0x100 DRAM module gating */
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) || defined(CONFIG_MACH_SUN50I)
|
|
+ u32 de_clk_cfg; /* 0x104 DE module clock */
|
|
+#else
|
|
u32 be0_clk_cfg; /* 0x104 BE0 module clock */
|
|
+#endif
|
|
u32 be1_clk_cfg; /* 0x108 BE1 module clock */
|
|
u32 fe0_clk_cfg; /* 0x10c FE0 module clock */
|
|
u32 fe1_clk_cfg; /* 0x110 FE1 module clock */
|
|
u32 mp_clk_cfg; /* 0x114 MP module clock */
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) || defined(CONFIG_MACH_SUN50I)
|
|
+ u32 tcon0_clk_cfg; /* 0x118 TCON0 module clock */
|
|
+ u32 tcon1_clk_cfg; /* 0x11c TCON1 module clock */
|
|
+#else
|
|
u32 lcd0_ch0_clk_cfg; /* 0x118 LCD0 CH0 module clock */
|
|
u32 lcd1_ch0_clk_cfg; /* 0x11c LCD1 CH0 module clock */
|
|
- u32 reserved14[3];
|
|
+#endif
|
|
+ u32 tve_clk_cfg; /* 0x120 TVE module clock */
|
|
+ u32 reserved14[2];
|
|
u32 lcd0_ch1_clk_cfg; /* 0x12c LCD0 CH1 module clock */
|
|
u32 lcd1_ch1_clk_cfg; /* 0x130 LCD1 CH1 module clock */
|
|
u32 csi0_clk_cfg; /* 0x134 CSI0 module clock */
|
|
@@ -85,7 +95,11 @@ struct sunxi_ccm_reg {
|
|
u32 dmic_clk_cfg; /* 0x148 Digital Mic module clock*/
|
|
u32 reserved15;
|
|
u32 hdmi_clk_cfg; /* 0x150 HDMI module clock */
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) || defined(CONFIG_MACH_SUN50I)
|
|
+ u32 hdmi_slow_clk_cfg; /* 0x154 HDMI slow module clock */
|
|
+#else
|
|
u32 ps_clk_cfg; /* 0x154 PS module clock */
|
|
+#endif
|
|
u32 mtc_clk_cfg; /* 0x158 MTC module clock */
|
|
u32 mbus0_clk_cfg; /* 0x15c MBUS0 module clock */
|
|
u32 mbus1_clk_cfg; /* 0x160 MBUS1 module clock */
|
|
@@ -220,6 +234,15 @@ struct sunxi_ccm_reg {
|
|
#define CCM_MIPI_PLL_CTRL_LDO_EN (0x3 << 22)
|
|
#define CCM_MIPI_PLL_CTRL_EN (0x1 << 31)
|
|
|
|
+#define CCM_PLL10_CTRL_M_SHIFT 0
|
|
+#define CCM_PLL10_CTRL_M_MASK (0xf << CCM_PLL10_CTRL_M_SHIFT)
|
|
+#define CCM_PLL10_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+#define CCM_PLL10_CTRL_N_SHIFT 8
|
|
+#define CCM_PLL10_CTRL_N_MASK (0x7f << CCM_PLL10_CTRL_N_SHIFT)
|
|
+#define CCM_PLL10_CTRL_N(n) ((((n) - 1) & 0x7f) << 8)
|
|
+#define CCM_PLL10_CTRL_INTEGER_MODE (0x1 << 24)
|
|
+#define CCM_PLL10_CTRL_EN (0x1 << 31)
|
|
+
|
|
#if defined(CONFIG_MACH_SUN50I)
|
|
#define CCM_PLL11_CTRL_N(n) ((((n) - 1) & 0x7f) << 8)
|
|
#else
|
|
@@ -271,9 +294,13 @@ struct sunxi_ccm_reg {
|
|
#define AHB_GATE_OFFSET_DRC0 25
|
|
#define AHB_GATE_OFFSET_DE_FE0 14
|
|
#define AHB_GATE_OFFSET_DE_BE0 12
|
|
+#define AHB_GATE_OFFSET_DE 12
|
|
#define AHB_GATE_OFFSET_HDMI 11
|
|
+#define AHB_GATE_OFFSET_TVE 9
|
|
#define AHB_GATE_OFFSET_LCD1 5
|
|
#define AHB_GATE_OFFSET_LCD0 4
|
|
+#define AHB_GATE_OFFSET_TCON1 4
|
|
+#define AHB_GATE_OFFSET_TCON0 3
|
|
|
|
#define CCM_MMC_CTRL_M(x) ((x) - 1)
|
|
#define CCM_MMC_CTRL_OCLK_DLY(x) ((x) << 8)
|
|
@@ -347,6 +374,15 @@ struct sunxi_ccm_reg {
|
|
#define CCM_LCD_CH0_CTRL_RST 0
|
|
#define CCM_LCD_CH0_CTRL_GATE (0x1 << 31)
|
|
|
|
+#define CCM_TVE_CTRL_GATE (0x1 << 31)
|
|
+#define CCM_TVE_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+
|
|
+#define CCM_TCON0_CTRL_GATE (0x1 << 31)
|
|
+#define CCM_TCON0_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+
|
|
+#define CCM_TCON1_CTRL_GATE (0x1 << 31)
|
|
+#define CCM_TCON1_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+
|
|
#define CCM_LCD_CH1_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
#define CCM_LCD_CH1_CTRL_HALF_SCLK1 0 /* no seperate sclk1 & 2 on sun6i */
|
|
#define CCM_LCD_CH1_CTRL_PLL3 (0 << 24)
|
|
@@ -356,6 +392,7 @@ struct sunxi_ccm_reg {
|
|
#define CCM_LCD_CH1_CTRL_GATE (0x1 << 31)
|
|
|
|
#define CCM_HDMI_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+#define CCM_HDMI_CTRL_M_MASK (0xf << 0)
|
|
#define CCM_HDMI_CTRL_PLL_MASK (3 << 24)
|
|
#define CCM_HDMI_CTRL_PLL3 (0 << 24)
|
|
#define CCM_HDMI_CTRL_PLL7 (1 << 24)
|
|
@@ -364,6 +401,8 @@ struct sunxi_ccm_reg {
|
|
#define CCM_HDMI_CTRL_DDC_GATE (0x1 << 30)
|
|
#define CCM_HDMI_CTRL_GATE (0x1 << 31)
|
|
|
|
+#define CCM_HDMI_SLOW_CTRL_DDC_GATE (1 << 31)
|
|
+
|
|
#if defined(CONFIG_MACH_SUN50I)
|
|
#define MBUS_CLK_DEFAULT 0x81000002 /* PLL6x2 / 3 */
|
|
#elif defined(CONFIG_MACH_SUN8I)
|
|
@@ -391,9 +430,14 @@ struct sunxi_ccm_reg {
|
|
#define AHB_RESET_OFFSET_DRC0 25
|
|
#define AHB_RESET_OFFSET_DE_FE0 14
|
|
#define AHB_RESET_OFFSET_DE_BE0 12
|
|
+#define AHB_RESET_OFFSET_DE 12
|
|
#define AHB_RESET_OFFSET_HDMI 11
|
|
+#define AHB_RESET_OFFSET_HDMI2 10
|
|
+#define AHB_RESET_OFFSET_TVE 9
|
|
#define AHB_RESET_OFFSET_LCD1 5
|
|
#define AHB_RESET_OFFSET_LCD0 4
|
|
+#define AHB_RESET_OFFSET_TCON1 4
|
|
+#define AHB_RESET_OFFSET_TCON0 3
|
|
|
|
/* ahb_reset2 offsets */
|
|
#define AHB_RESET_OFFSET_EPHY 2
|
|
@@ -416,6 +460,13 @@ struct sunxi_ccm_reg {
|
|
#define CCM_DE_CTRL_PLL10 (5 << 24)
|
|
#define CCM_DE_CTRL_GATE (1 << 31)
|
|
|
|
+/* CCM bits common to all Display Engine 2.0 clock ctrl regs */
|
|
+#define CCM_DE2_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+#define CCM_DE2_CTRL_PLL_MASK (3 << 24)
|
|
+#define CCM_DE2_CTRL_PLL6_2X (0 << 24)
|
|
+#define CCM_DE2_CTRL_PLL10 (1 << 24)
|
|
+#define CCM_DE2_CTRL_GATE (1 << 31)
|
|
+
|
|
/* CCU security switch, H3 only */
|
|
#define CCM_SEC_SWITCH_MBUS_NONSEC (1 << 2)
|
|
#define CCM_SEC_SWITCH_BUS_NONSEC (1 << 1)
|
|
@@ -424,7 +475,9 @@ struct sunxi_ccm_reg {
|
|
#ifndef __ASSEMBLY__
|
|
void clock_set_pll1(unsigned int hz);
|
|
void clock_set_pll3(unsigned int hz);
|
|
+void clock_set_pll3_factors(int m, int n);
|
|
void clock_set_pll5(unsigned int clk, bool sigma_delta_enable);
|
|
+void clock_set_pll10(unsigned int hz);
|
|
void clock_set_pll11(unsigned int clk, bool sigma_delta_enable);
|
|
void clock_set_mipi_pll(unsigned int hz);
|
|
unsigned int clock_get_pll3(void);
|
|
diff --git a/u-boot/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/u-boot/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
|
|
index 3c85222..4a72dae 100644
|
|
--- a/u-boot/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
|
|
+++ b/u-boot/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
|
|
@@ -18,6 +18,8 @@
|
|
#define SUNXI_SRAM_D_BASE 0x00010000 /* 4 kiB */
|
|
#define SUNXI_SRAM_B_BASE 0x00020000 /* 64 kiB (secure) */
|
|
|
|
+#define SUNXI_DE2_BASE 0x01000000
|
|
+
|
|
#ifdef CONFIG_MACH_SUN8I_A83T
|
|
#define SUNXI_CPUCFG_BASE 0x01700000
|
|
#endif
|
|
@@ -32,7 +34,9 @@
|
|
#define SUNXI_MS_BASE 0x01c07000
|
|
#define SUNXI_TVD_BASE 0x01c08000
|
|
#define SUNXI_CSI0_BASE 0x01c09000
|
|
+#if !defined(CONFIG_MACH_SUN8I_H3) && !defined(CONFIG_MACH_SUN50I_H5)
|
|
#define SUNXI_TVE0_BASE 0x01c0a000
|
|
+#endif
|
|
#define SUNXI_EMAC_BASE 0x01c0b000
|
|
#define SUNXI_LCD0_BASE 0x01c0C000
|
|
#define SUNXI_LCD1_BASE 0x01c0d000
|
|
@@ -46,7 +50,9 @@
|
|
#define SUNXI_USB1_BASE 0x01c14000
|
|
#endif
|
|
#define SUNXI_SS_BASE 0x01c15000
|
|
+#if !defined(CONFIG_MACH_SUN8I_H3) && !defined(CONFIG_MACH_SUN50I)
|
|
#define SUNXI_HDMI_BASE 0x01c16000
|
|
+#endif
|
|
#define SUNXI_SPI2_BASE 0x01c17000
|
|
#define SUNXI_SATA_BASE 0x01c18000
|
|
#ifdef CONFIG_SUNXI_GEN_SUN4I
|
|
@@ -157,12 +163,21 @@ defined(CONFIG_MACH_SUN50I)
|
|
#define SUNXI_SRAM_C_BASE 0x01d00000
|
|
|
|
#define SUNXI_DE_FE0_BASE 0x01e00000
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) && !defined(CONFIG_MACH_SUN50I_H5)
|
|
+#define SUNXI_TVE0_BASE 0x01e00000
|
|
+#else
|
|
+#define SUNXI_TVE0_BASE 0x01e40000
|
|
+#endif
|
|
#define SUNXI_DE_FE1_BASE 0x01e20000
|
|
#define SUNXI_DE_BE0_BASE 0x01e60000
|
|
#define SUNXI_DE_BE1_BASE 0x01e40000
|
|
#define SUNXI_MP_BASE 0x01e80000
|
|
#define SUNXI_AVG_BASE 0x01ea0000
|
|
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) || defined(CONFIG_MACH_SUN50I)
|
|
+#define SUNXI_HDMI_BASE 0x01ee0000
|
|
+#endif
|
|
+
|
|
#define SUNXI_RTC_BASE 0x01f00000
|
|
#define SUNXI_PRCM_BASE 0x01f01400
|
|
|
|
diff --git a/u-boot/arch/arm/include/asm/arch-sunxi/display2.h b/u-boot/arch/arm/include/asm/arch-sunxi/display2.h
|
|
new file mode 100644
|
|
index 0000000..6021be9
|
|
--- /dev/null
|
|
+++ b/u-boot/arch/arm/include/asm/arch-sunxi/display2.h
|
|
@@ -0,0 +1,490 @@
|
|
+/*
|
|
+ * Sunxi platform display controller register and constant defines
|
|
+ *
|
|
+ * (C) Copyright 2016 Jernej Skrabec <jernej.skrabec@siol.net>
|
|
+ *
|
|
+ * Based on Linux DRM driver defines:
|
|
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
|
|
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
|
|
+ *
|
|
+ * Based on display.h:
|
|
+ * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+
|
|
+#ifndef _SUNXI_DISPLAY2_H
|
|
+#define _SUNXI_DISPLAY2_H
|
|
+
|
|
+struct sunxi_lcdc_reg {
|
|
+ u32 ctrl; /* 0x00 */
|
|
+ u32 int0; /* 0x04 */
|
|
+ u32 int1; /* 0x08 */
|
|
+ u8 res0[0x04]; /* 0x0c */
|
|
+ u32 tcon0_frm_ctrl; /* 0x10 */
|
|
+ u32 tcon0_frm_seed[6]; /* 0x14 */
|
|
+ u32 tcon0_frm_table[4]; /* 0x2c */
|
|
+ u8 res1[4]; /* 0x3c */
|
|
+ u32 tcon0_ctrl; /* 0x40 */
|
|
+ u32 tcon0_dclk; /* 0x44 */
|
|
+ u32 tcon0_timing_active; /* 0x48 */
|
|
+ u32 tcon0_timing_h; /* 0x4c */
|
|
+ u32 tcon0_timing_v; /* 0x50 */
|
|
+ u32 tcon0_timing_sync; /* 0x54 */
|
|
+ u32 tcon0_hv_intf; /* 0x58 */
|
|
+ u8 res2[0x04]; /* 0x5c */
|
|
+ u32 tcon0_cpu_intf; /* 0x60 */
|
|
+ u32 tcon0_cpu_wr_dat; /* 0x64 */
|
|
+ u32 tcon0_cpu_rd_dat0; /* 0x68 */
|
|
+ u32 tcon0_cpu_rd_dat1; /* 0x6c */
|
|
+ u32 tcon0_ttl_timing0; /* 0x70 */
|
|
+ u32 tcon0_ttl_timing1; /* 0x74 */
|
|
+ u32 tcon0_ttl_timing2; /* 0x78 */
|
|
+ u32 tcon0_ttl_timing3; /* 0x7c */
|
|
+ u32 tcon0_ttl_timing4; /* 0x80 */
|
|
+ u32 tcon0_lvds_intf; /* 0x84 */
|
|
+ u32 tcon0_io_polarity; /* 0x88 */
|
|
+ u32 tcon0_io_tristate; /* 0x8c */
|
|
+ u32 tcon1_ctrl; /* 0x90 */
|
|
+ u32 tcon1_timing_source; /* 0x94 */
|
|
+ u32 tcon1_timing_scale; /* 0x98 */
|
|
+ u32 tcon1_timing_out; /* 0x9c */
|
|
+ u32 tcon1_timing_h; /* 0xa0 */
|
|
+ u32 tcon1_timing_v; /* 0xa4 */
|
|
+ u32 tcon1_timing_sync; /* 0xa8 */
|
|
+ u8 res3[0x44]; /* 0xac */
|
|
+ u32 tcon1_io_polarity; /* 0xf0 */
|
|
+ u32 tcon1_io_tristate; /* 0xf4 */
|
|
+ u8 res4[0x108]; /* 0xf8 */
|
|
+ u32 mux_ctrl; /* 0x200 */
|
|
+};
|
|
+
|
|
+/* internal clock settings */
|
|
+struct de_clk {
|
|
+ u32 gate_cfg;
|
|
+ u32 bus_cfg;
|
|
+ u32 rst_cfg;
|
|
+ u32 div_cfg;
|
|
+ u32 sel_cfg;
|
|
+};
|
|
+
|
|
+/* global control */
|
|
+struct de_glb {
|
|
+ u32 ctl;
|
|
+ u32 status;
|
|
+ u32 dbuff;
|
|
+ u32 size;
|
|
+};
|
|
+
|
|
+/* alpha blending */
|
|
+struct de_bld {
|
|
+ u32 fcolor_ctl; /* 00 */
|
|
+ struct {
|
|
+ u32 fcolor;
|
|
+ u32 insize;
|
|
+ u32 offset;
|
|
+ u32 dum;
|
|
+ } attr[4];
|
|
+ u32 dum0[15]; /* (end of clear offset) */
|
|
+ u32 route; /* 80 */
|
|
+ u32 premultiply;
|
|
+ u32 bkcolor;
|
|
+ u32 output_size;
|
|
+ u32 bld_mode[4];
|
|
+ u32 dum1[4];
|
|
+ u32 ck_ctl; /* b0 */
|
|
+ u32 ck_cfg;
|
|
+ u32 dum2[2];
|
|
+ u32 ck_max[4]; /* c0 */
|
|
+ u32 dum3[4];
|
|
+ u32 ck_min[4]; /* e0 */
|
|
+ u32 dum4[3];
|
|
+ u32 out_ctl; /* fc */
|
|
+};
|
|
+
|
|
+/* VI channel */
|
|
+struct de_vi {
|
|
+ struct {
|
|
+ u32 attr;
|
|
+ u32 size;
|
|
+ u32 coord;
|
|
+ u32 pitch[3];
|
|
+ u32 top_laddr[3];
|
|
+ u32 bot_laddr[3];
|
|
+ } cfg[4];
|
|
+ u32 fcolor[4]; /* c0 */
|
|
+ u32 top_haddr[3]; /* d0 */
|
|
+ u32 bot_haddr[3]; /* dc */
|
|
+ u32 ovl_size[2]; /* e8 */
|
|
+ u32 hori[2]; /* f0 */
|
|
+ u32 vert[2]; /* f8 */
|
|
+};
|
|
+
|
|
+struct de_ui {
|
|
+ struct {
|
|
+ u32 attr;
|
|
+ u32 size;
|
|
+ u32 coord;
|
|
+ u32 pitch;
|
|
+ u32 top_laddr;
|
|
+ u32 bot_laddr;
|
|
+ u32 fcolor;
|
|
+ u32 dum;
|
|
+ } cfg[4]; /* 00 */
|
|
+ u32 top_haddr; /* 80 */
|
|
+ u32 bot_haddr;
|
|
+ u32 ovl_size; /* 88 */
|
|
+};
|
|
+
|
|
+struct de_csc {
|
|
+ u32 csc_ctl;
|
|
+ u32 rsv[3];
|
|
+ u32 coef11;
|
|
+ u32 coef12;
|
|
+ u32 coef13;
|
|
+ u32 coef14;
|
|
+ u32 coef21;
|
|
+ u32 coef22;
|
|
+ u32 coef23;
|
|
+ u32 coef24;
|
|
+ u32 coef31;
|
|
+ u32 coef32;
|
|
+ u32 coef33;
|
|
+ u32 coef34;
|
|
+ u32 alpha;
|
|
+};
|
|
+
|
|
+struct sunxi_dwc_hdmi {
|
|
+ u8 reserved0[0x100];
|
|
+ u8 ih_fc_stat0;
|
|
+ u8 ih_fc_stat1;
|
|
+ u8 ih_fc_stat2;
|
|
+ u8 ih_as_stat0;
|
|
+ u8 ih_phy_stat0;
|
|
+ u8 ih_i2cm_stat0;
|
|
+ u8 ih_cec_stat0;
|
|
+ u8 ih_vp_stat0;
|
|
+ u8 ih_i2cmphy_stat0;
|
|
+ u8 ih_ahbdmaaud_stat0;
|
|
+ u8 reserved1[0x17f-0x109];
|
|
+ u8 ih_mute_fc_stat0;
|
|
+ u8 ih_mute_fc_stat1;
|
|
+ u8 ih_mute_fc_stat2;
|
|
+ u8 ih_mute_as_stat0;
|
|
+ u8 ih_mute_phy_stat0;
|
|
+ u8 ih_mute_i2cm_stat0;
|
|
+ u8 ih_mute_cec_stat0;
|
|
+ u8 ih_mute_vp_stat0;
|
|
+ u8 ih_mute_i2cmphy_stat0;
|
|
+ u8 ih_mute_ahbdmaaud_stat0;
|
|
+ u8 reserved2[0x1fe - 0x189];
|
|
+ u8 ih_mute;
|
|
+ u8 tx_invid0;
|
|
+ u8 tx_instuffing;
|
|
+ u8 tx_gydata0;
|
|
+ u8 tx_gydata1;
|
|
+ u8 tx_rcrdata0;
|
|
+ u8 tx_rcrdata1;
|
|
+ u8 tx_bcbdata0;
|
|
+ u8 tx_bcbdata1;
|
|
+ u8 reserved3[0x7ff-0x207];
|
|
+ u8 vp_status;
|
|
+ u8 vp_pr_cd;
|
|
+ u8 vp_stuff;
|
|
+ u8 vp_remap;
|
|
+ u8 vp_conf;
|
|
+ u8 vp_stat;
|
|
+ u8 vp_int;
|
|
+ u8 vp_mask;
|
|
+ u8 vp_pol;
|
|
+ u8 reserved4[0xfff-0x808];
|
|
+ u8 fc_invidconf;
|
|
+ u8 fc_inhactv0;
|
|
+ u8 fc_inhactv1;
|
|
+ u8 fc_inhblank0;
|
|
+ u8 fc_inhblank1;
|
|
+ u8 fc_invactv0;
|
|
+ u8 fc_invactv1;
|
|
+ u8 fc_invblank;
|
|
+ u8 fc_hsyncindelay0;
|
|
+ u8 fc_hsyncindelay1;
|
|
+ u8 fc_hsyncinwidth0;
|
|
+ u8 fc_hsyncinwidth1;
|
|
+ u8 fc_vsyncindelay;
|
|
+ u8 fc_vsyncinwidth;
|
|
+ u8 fc_infreq0;
|
|
+ u8 fc_infreq1;
|
|
+ u8 fc_infreq2;
|
|
+ u8 fc_ctrldur;
|
|
+ u8 fc_exctrldur;
|
|
+ u8 fc_exctrlspac;
|
|
+ u8 fc_ch0pream;
|
|
+ u8 fc_ch1pream;
|
|
+ u8 fc_ch2pream;
|
|
+ u8 fc_aviconf3;
|
|
+ u8 fc_gcp;
|
|
+ u8 fc_aviconf0;
|
|
+ u8 fc_aviconf1;
|
|
+ u8 fc_aviconf2;
|
|
+ u8 fc_avivid;
|
|
+ u8 fc_avietb0;
|
|
+ u8 fc_avietb1;
|
|
+ u8 fc_avisbb0;
|
|
+ u8 fc_avisbb1;
|
|
+ u8 fc_avielb0;
|
|
+ u8 fc_avielb1;
|
|
+ u8 fc_avisrb0;
|
|
+ u8 fc_avisrb1;
|
|
+ u8 fc_audiconf0;
|
|
+ u8 fc_audiconf1;
|
|
+ u8 fc_audiconf2;
|
|
+ u8 fc_audiconf3;
|
|
+ u8 fc_vsdieeeid0;
|
|
+ u8 fc_vsdsize;
|
|
+ u8 reserved5[0x30ff-0x102a];
|
|
+ u8 aud_conf0;
|
|
+ u8 aud_conf1;
|
|
+ u8 aud_int;
|
|
+ u8 aud_conf2;
|
|
+ u8 aud_int1;
|
|
+ u8 reserved6[0x31ff-0x3104];
|
|
+ u8 aud_n1;
|
|
+ u8 aud_n2;
|
|
+ u8 aud_n3;
|
|
+ u8 aud_cts1;
|
|
+ u8 aud_cts2;
|
|
+ u8 aud_cts3;
|
|
+ u8 aud_inputclkfs;
|
|
+ u8 reserved7[0x3fff-0x3206];
|
|
+ u8 mc_sfrdiv;
|
|
+ u8 mc_clkdis;
|
|
+ u8 mc_swrstz;
|
|
+ u8 mc_opctrl;
|
|
+ u8 mc_flowctrl;
|
|
+ u8 mc_phyrstz;
|
|
+ u8 mc_lockonclock;
|
|
+ u8 mc_heacphy_rst;
|
|
+ u8 reserved8[0x40ff-0x4007];
|
|
+ u8 csc_cfg;
|
|
+ u8 csc_scale;
|
|
+ struct {
|
|
+ u8 msb;
|
|
+ u8 lsb;
|
|
+ } csc_coef[3][4];
|
|
+ u8 reserved9[0x7dff-0x4119];
|
|
+ u8 i2cm_slave;
|
|
+ u8 i2c_address;
|
|
+ u8 i2cm_datao;
|
|
+ u8 i2cm_datai;
|
|
+ u8 i2cm_operation;
|
|
+ u8 i2cm_int;
|
|
+ u8 i2cm_ctlint;
|
|
+ u8 i2cm_div;
|
|
+ u8 i2cm_segaddr;
|
|
+ u8 i2cm_softrstz;
|
|
+ u8 i2cm_segptr;
|
|
+ u8 i2cm_ss_scl_hcnt_1_addr;
|
|
+ u8 i2cm_ss_scl_hcnt_0_addr;
|
|
+ u8 i2cm_ss_scl_lcnt_1_addr;
|
|
+ u8 i2cm_ss_scl_lcnt_0_addr;
|
|
+ u8 i2cm_fs_scl_hcnt_1_addr;
|
|
+ u8 i2cm_fs_scl_hcnt_0_addr;
|
|
+ u8 i2cm_fs_scl_lcnt_1_addr;
|
|
+ u8 i2cm_fs_scl_lcnt_0_addr;
|
|
+ u8 reserved10[0xffff-0x7e12];
|
|
+ u32 phy_pol;
|
|
+ u32 phy_reserved11[3];
|
|
+ u32 phy_read_en;
|
|
+ u32 phy_unscramble;
|
|
+ u32 reserved12[2];
|
|
+ u32 phy_ctrl;
|
|
+ u32 phy_unk1;
|
|
+ u32 phy_unk2;
|
|
+ u32 phy_pll;
|
|
+ u32 phy_clk;
|
|
+ u32 phy_unk3;
|
|
+ u32 phy_status;
|
|
+};
|
|
+
|
|
+struct sunxi_tve_reg {
|
|
+ u32 gctrl; /* 0x000 */
|
|
+ u32 cfg0; /* 0x004 */
|
|
+ u32 dac_cfg0; /* 0x008 */
|
|
+ u32 filter; /* 0x00c */
|
|
+ u32 chroma_freq; /* 0x010 */
|
|
+ u32 porch_num; /* 0x014 */
|
|
+ u32 unknown0; /* 0x018 */
|
|
+ u32 line_num; /* 0x01c */
|
|
+ u32 blank_black_level; /* 0x020 */
|
|
+ u32 unknown1; /* 0x024, seems to be 1 byte per dac */
|
|
+ u8 res0[8]; /* 0x028 */
|
|
+ u32 auto_detect_en; /* 0x030 */
|
|
+ u32 auto_detect_int_status; /* 0x034 */
|
|
+ u32 auto_detect_status; /* 0x038 */
|
|
+ u32 auto_detect_debounce; /* 0x03c */
|
|
+ u32 csc_reg0; /* 0x040 */
|
|
+ u32 csc_reg1; /* 0x044 */
|
|
+ u32 csc_reg2; /* 0x048 */
|
|
+ u32 csc_reg3; /* 0x04c */
|
|
+ u8 res1[0xb0]; /* 0x050 */
|
|
+ u32 color_burst; /* 0x100 */
|
|
+ u32 vsync_num; /* 0x104 */
|
|
+ u32 notch_freq; /* 0x108 */
|
|
+ u32 cbr_level; /* 0x10c */
|
|
+ u32 burst_phase; /* 0x110 */
|
|
+ u32 burst_width; /* 0x114 */
|
|
+ u32 unknown2; /* 0x118 */
|
|
+ u32 sync_vbi_level; /* 0x11c */
|
|
+ u32 white_level; /* 0x120 */
|
|
+ u32 active_num; /* 0x124 */
|
|
+ u32 chroma_bw_gain; /* 0x128 */
|
|
+ u32 notch_width; /* 0x12c */
|
|
+ u32 resync_num; /* 0x130 */
|
|
+ u32 slave_para; /* 0x134 */
|
|
+ u32 cfg1; /* 0x138 */
|
|
+ u32 cfg2; /* 0x13c */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * DE register constants.
|
|
+ */
|
|
+#define SUNXI_DE2_MUX0_BASE (u8 *)(SUNXI_DE2_BASE + 0x100000)
|
|
+#define SUNXI_DE2_MUX1_BASE (u8 *)(SUNXI_DE2_BASE + 0x200000)
|
|
+
|
|
+#define SUNXI_DE2_MUX_GLB_REGS 0x00000
|
|
+#define SUNXI_DE2_MUX_BLD_REGS 0x01000
|
|
+#define SUNXI_DE2_MUX_CHAN_REGS 0x02000
|
|
+#define SUNXI_DE2_MUX_CHAN_SZ 0x1000
|
|
+#define SUNXI_DE2_MUX_VSU_REGS 0x20000
|
|
+#define SUNXI_DE2_MUX_GSU1_REGS 0x30000
|
|
+#define SUNXI_DE2_MUX_GSU2_REGS 0x40000
|
|
+#define SUNXI_DE2_MUX_GSU3_REGS 0x50000
|
|
+#define SUNXI_DE2_MUX_FCE_REGS 0xa0000
|
|
+#define SUNXI_DE2_MUX_BWS_REGS 0xa2000
|
|
+#define SUNXI_DE2_MUX_LTI_REGS 0xa4000
|
|
+#define SUNXI_DE2_MUX_PEAK_REGS 0xa6000
|
|
+#define SUNXI_DE2_MUX_ASE_REGS 0xa8000
|
|
+#define SUNXI_DE2_MUX_FCC_REGS 0xaa000
|
|
+#define SUNXI_DE2_MUX_DCSC_REGS 0xb0000
|
|
+
|
|
+#define SUNXI_DE2_FORMAT_ARGB_8888 0
|
|
+#define SUNXI_DE2_FORMAT_BGRA_8888 3
|
|
+#define SUNXI_DE2_FORMAT_XRGB_8888 4
|
|
+#define SUNXI_DE2_FORMAT_RGB_888 8
|
|
+#define SUNXI_DE2_FORMAT_BGR_888 9
|
|
+
|
|
+#define SUNXI_DE2_MUX_GLB_CTL_RT_EN (1 << 0)
|
|
+
|
|
+#define SUNXI_DE2_UI_CFG_ATTR_EN (1 << 0)
|
|
+#define SUNXI_DE2_UI_CFG_ATTR_ALPMOD(m) ((m & 3) << 1)
|
|
+#define SUNXI_DE2_UI_CFG_ATTR_FMT(f) ((f & 0xf) << 8)
|
|
+#define SUNXI_DE2_UI_CFG_ATTR_ALPHA(a) ((a & 0xff) << 24)
|
|
+
|
|
+#define SUNXI_DE2_WH(w, h) (((h - 1) << 16) | (w - 1))
|
|
+
|
|
+/*
|
|
+ * LCDC register constants.
|
|
+ */
|
|
+#define SUNXI_LCDC_X(x) (((x) - 1) << 16)
|
|
+#define SUNXI_LCDC_Y(y) (((y) - 1) << 0)
|
|
+#define SUNXI_LCDC_CTRL_TCON_ENABLE (1 << 31)
|
|
+#define SUNXI_LCDC_TCON0_CTRL_ENABLE (1 << 31)
|
|
+#define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n) (((n) & 0x1f) << 4)
|
|
+#define SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE (1 << 20)
|
|
+#define SUNXI_LCDC_TCON1_CTRL_ENABLE (1 << 31)
|
|
+#define SUNXI_LCDC_TCON1_TIMING_H_BP(n) (((n) - 1) << 0)
|
|
+#define SUNXI_LCDC_TCON1_TIMING_H_TOTAL(n) (((n) - 1) << 16)
|
|
+#define SUNXI_LCDC_TCON1_TIMING_V_BP(n) (((n) - 1) << 0)
|
|
+#define SUNXI_LCDC_TCON1_TIMING_V_TOTAL(n) ((n) << 16)
|
|
+
|
|
+/*
|
|
+ * HDMI register constants.
|
|
+ */
|
|
+#define HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT 0x02
|
|
+#define HDMI_IH_MUTE_MUTE_ALL_INTERRUPT 0x01
|
|
+
|
|
+#define HDMI_TX_INSTUFFING_BDBDATA_STUFFING_EN 0x04
|
|
+#define HDMI_TX_INSTUFFING_RCRDATA_STUFFING_EN 0x02
|
|
+#define HDMI_TX_INSTUFFING_GYDATA_STUFFING_EN 0x01
|
|
+
|
|
+#define HDMI_FC_INVIDCONF_DVI_MODE_HDMI 0x08
|
|
+#define HDMI_FC_INVIDCONF_DE_IN_POL_ACTIVE_HIGH 0x10
|
|
+
|
|
+#define HDMI_FC_AVICONF0_ACTIVE_FORMAT 0x40
|
|
+#define HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
|
|
+
|
|
+#define HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE 0x08
|
|
+#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80
|
|
+
|
|
+#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE 0x02
|
|
+
|
|
+#define HDMI_MC_FLOWCTRL_CSC_BYPASS 0x00
|
|
+
|
|
+#define HDMI_I2CM_CTLINT_ADDR_NACK_POL 0x80
|
|
+#define HDMI_I2CM_CTLINT_ADDR_NACK_MSK 0x40
|
|
+#define HDMI_I2CM_CTLINT_ADDR_ARB_POL 0x08
|
|
+#define HDMI_I2CM_CTLINT_ADDR_ARB_MSK 0x04
|
|
+
|
|
+#define HMDI_DDC_CTRL_RESET (1 << 0)
|
|
+#define HMDI_DDC_ADDR_SLAVE_ADDR (0x50 << 0)
|
|
+#define HMDI_DDC_ADDR_SEG_ADDR (0x30 << 0)
|
|
+
|
|
+#define SUNXI_HDMI_HPD_DETECT (1 << 19)
|
|
+
|
|
+/*
|
|
+ * TVE register constants.
|
|
+ */
|
|
+#define SUNXI_TVE_GCTRL_ENABLE (1 << 0)
|
|
+/*
|
|
+ * Select input 0 to disable dac, 1 - 4 to feed dac from tve0, 5 - 8 to feed
|
|
+ * dac from tve1. When using tve1 the mux value must be written to both tve0's
|
|
+ * and tve1's gctrl reg.
|
|
+ */
|
|
+#define SUNXI_TVE_GCTRL_DAC_INPUT_MASK(dac) (0xf << (((dac) + 1) * 4))
|
|
+#define SUNXI_TVE_GCTRL_DAC_INPUT(dac, sel) ((sel) << (((dac) + 1) * 4))
|
|
+
|
|
+#define SUNXI_TVE_CFG0_PAL 0x07070001
|
|
+#define SUNXI_TVE_CFG0_NTSC 0x07070000
|
|
+#define SUNXI_TVE_DAC_CFG0_COMPOSITE 0x433f0009
|
|
+#define SUNXI_TVE_FILTER_COMPOSITE 0x00000120
|
|
+#define SUNXI_TVE_CHROMA_FREQ_PAL_M 0x21e6efe3
|
|
+#define SUNXI_TVE_CHROMA_FREQ_PAL_NC 0x21f69446
|
|
+#define SUNXI_TVE_PORCH_NUM_PAL 0x008a0018
|
|
+#define SUNXI_TVE_PORCH_NUM_NTSC 0x00760020
|
|
+#define SUNXI_TVE_LINE_NUM_PAL 0x00160271
|
|
+#define SUNXI_TVE_LINE_NUM_NTSC 0x0016020d
|
|
+#define SUNXI_TVE_BLANK_BLACK_LEVEL_PAL 0x00fc00fc
|
|
+#define SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC 0x00f0011a
|
|
+#define SUNXI_TVE_UNKNOWN1_VGA 0x00000000
|
|
+#define SUNXI_TVE_UNKNOWN1_COMPOSITE 0x18181818
|
|
+#define SUNXI_TVE_AUTO_DETECT_EN_DET_EN(dac) (1 << ((dac) + 0))
|
|
+#define SUNXI_TVE_AUTO_DETECT_EN_INT_EN(dac) (1 << ((dac) + 16))
|
|
+#define SUNXI_TVE_AUTO_DETECT_INT_STATUS(dac) (1 << ((dac) + 0))
|
|
+#define SUNXI_TVE_AUTO_DETECT_STATUS_SHIFT(dac) ((dac) * 8)
|
|
+#define SUNXI_TVE_AUTO_DETECT_STATUS_MASK(dac) (3 << ((dac) * 8))
|
|
+#define SUNXI_TVE_AUTO_DETECT_STATUS_NONE 0
|
|
+#define SUNXI_TVE_AUTO_DETECT_STATUS_CONNECTED 1
|
|
+#define SUNXI_TVE_AUTO_DETECT_STATUS_SHORT_GND 3
|
|
+#define SUNXI_TVE_AUTO_DETECT_DEBOUNCE_SHIFT(d) ((d) * 8)
|
|
+#define SUNXI_TVE_AUTO_DETECT_DEBOUNCE_MASK(d) (0xf << ((d) * 8))
|
|
+#define SUNXI_TVE_CSC_REG0_ENABLE (1 << 31)
|
|
+#define SUNXI_TVE_CSC_REG0 0x08440832
|
|
+#define SUNXI_TVE_CSC_REG1 0x3b6dace1
|
|
+#define SUNXI_TVE_CSC_REG2 0x0e1d13dc
|
|
+#define SUNXI_TVE_CSC_REG3 0x00108080
|
|
+#define SUNXI_TVE_COLOR_BURST_PAL_M 0x00000000
|
|
+#define SUNXI_TVE_CBR_LEVEL_PAL 0x00002828
|
|
+#define SUNXI_TVE_CBR_LEVEL_NTSC 0x0000004f
|
|
+#define SUNXI_TVE_BURST_PHASE_NTSC 0x00000000
|
|
+#define SUNXI_TVE_BURST_WIDTH_COMPOSITE 0x0016447e
|
|
+#define SUNXI_TVE_UNKNOWN2_PAL 0x0000e0e0
|
|
+#define SUNXI_TVE_UNKNOWN2_NTSC 0x0000a0a0
|
|
+#define SUNXI_TVE_SYNC_VBI_LEVEL_NTSC 0x001000f0
|
|
+#define SUNXI_TVE_ACTIVE_NUM_COMPOSITE 0x000005a0
|
|
+#define SUNXI_TVE_CHROMA_BW_GAIN_COMP 0x00000002
|
|
+#define SUNXI_TVE_NOTCH_WIDTH_COMPOSITE 0x00000101
|
|
+#define SUNXI_TVE_RESYNC_NUM_PAL 0x800d000c
|
|
+#define SUNXI_TVE_RESYNC_NUM_NTSC 0x000e000c
|
|
+#define SUNXI_TVE_SLAVE_PARA_COMPOSITE 0x00000000
|
|
+
|
|
+#endif /* _SUNXI_DISPLAY_H */
|
|
diff --git a/u-boot/arch/arm/mach-sunxi/clock_sun6i.c b/u-boot/arch/arm/mach-sunxi/clock_sun6i.c
|
|
index d123b3a..7af7791 100644
|
|
--- a/u-boot/arch/arm/mach-sunxi/clock_sun6i.c
|
|
+++ b/u-boot/arch/arm/mach-sunxi/clock_sun6i.c
|
|
@@ -145,6 +145,17 @@ void clock_set_pll3(unsigned int clk)
|
|
&ccm->pll3_cfg);
|
|
}
|
|
|
|
+void clock_set_pll3_factors(int m, int n)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+
|
|
+ /* PLL3 rate = 24000000 * n / m */
|
|
+ writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE |
|
|
+ CCM_PLL3_CTRL_N(n) | CCM_PLL3_CTRL_M(m),
|
|
+ &ccm->pll3_cfg);
|
|
+}
|
|
+
|
|
void clock_set_pll5(unsigned int clk, bool sigma_delta_enable)
|
|
{
|
|
struct sunxi_ccm_reg * const ccm =
|
|
@@ -217,6 +228,23 @@ done:
|
|
}
|
|
#endif
|
|
|
|
+void clock_set_pll10(unsigned int clk)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ const int m = 2; /* 12 MHz steps */
|
|
+
|
|
+ if (clk == 0) {
|
|
+ clrbits_le32(&ccm->pll10_cfg, CCM_PLL10_CTRL_EN);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* PLL10 rate = 24000000 * n / m */
|
|
+ writel(CCM_PLL10_CTRL_EN | CCM_PLL10_CTRL_INTEGER_MODE |
|
|
+ CCM_PLL10_CTRL_N(clk / (24000000 / m)) | CCM_PLL10_CTRL_M(m),
|
|
+ &ccm->pll10_cfg);
|
|
+}
|
|
+
|
|
#if defined(CONFIG_MACH_SUN8I_A33) || defined(CONFIG_MACH_SUN50I)
|
|
void clock_set_pll11(unsigned int clk, bool sigma_delta_enable)
|
|
{
|
|
diff --git a/u-boot/board/sunxi/Kconfig b/u-boot/board/sunxi/Kconfig
|
|
index 37b4252..8ba8644 100644
|
|
--- a/u-boot/board/sunxi/Kconfig
|
|
+++ b/u-boot/board/sunxi/Kconfig
|
|
@@ -485,7 +485,7 @@ config AXP_GPIO
|
|
|
|
config VIDEO
|
|
bool "Enable graphical uboot console on HDMI, LCD or VGA"
|
|
- depends on !MACH_SUN8I_A83T && !MACH_SUN8I_H3 && !MACH_SUN9I && !MACH_SUN50I
|
|
+ depends on !MACH_SUN8I_A83T && !MACH_SUN9I
|
|
default y
|
|
---help---
|
|
Say Y here to add support for using a cfb console on the HDMI, LCD
|
|
@@ -494,7 +494,7 @@ config VIDEO
|
|
|
|
config VIDEO_HDMI
|
|
bool "HDMI output support"
|
|
- depends on VIDEO && !MACH_SUN8I
|
|
+ depends on VIDEO && !MACH_SUN8I_A23 && !MACH_SUN8I_A33
|
|
default y
|
|
---help---
|
|
Say Y here to add support for outputting video over HDMI.
|
|
@@ -535,7 +535,7 @@ config VIDEO_VGA_EXTERNAL_DAC_EN
|
|
|
|
config VIDEO_COMPOSITE
|
|
bool "Composite video output support"
|
|
- depends on VIDEO && (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I)
|
|
+ depends on VIDEO && (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I_H3 || MACH_SUN50I_H5)
|
|
default n
|
|
---help---
|
|
Say Y here to add support for outputting composite video.
|
|
diff --git a/u-boot/configs/orangepi_pc2_defconfig b/u-boot/configs/orangepi_pc2_defconfig
|
|
index d17e4d3..f65a321 100644
|
|
--- a/u-boot/configs/orangepi_pc2_defconfig
|
|
+++ b/u-boot/configs/orangepi_pc2_defconfig
|
|
@@ -12,3 +12,5 @@ CONFIG_CONSOLE_MUX=y
|
|
# CONFIG_CMD_FPGA is not set
|
|
CONFIG_SUN8I_EMAC=y
|
|
CONFIG_USB_EHCI_HCD=y
|
|
+CONFIG_VIDEO=y
|
|
+CONFIG_VIDEO_COMPOSITE=y
|
|
diff --git a/u-boot/configs/pine64_plus_defconfig b/u-boot/configs/pine64_plus_defconfig
|
|
index a76f66a..9e2c3be 100644
|
|
--- a/u-boot/configs/pine64_plus_defconfig
|
|
+++ b/u-boot/configs/pine64_plus_defconfig
|
|
@@ -11,3 +11,4 @@ CONFIG_SPL=y
|
|
# CONFIG_CMD_FPGA is not set
|
|
CONFIG_SUN8I_EMAC=y
|
|
CONFIG_USB_EHCI_HCD=y
|
|
+CONFIG_VIDEO=y
|
|
diff --git a/u-boot/drivers/video/Makefile b/u-boot/drivers/video/Makefile
|
|
index db34904..3097b82 100644
|
|
--- a/u-boot/drivers/video/Makefile
|
|
+++ b/u-boot/drivers/video/Makefile
|
|
@@ -52,6 +52,7 @@ obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
|
|
obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
|
|
obj-$(CONFIG_VIDEO_SM501) += sm501.o
|
|
obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o videomodes.o
|
|
+obj-$(CONFIG_VIDEO_SUNXI2) += sunxi_display2.o videomodes.o
|
|
obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
|
|
obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
|
|
obj-$(CONFIG_VIDEO_VESA) += vesa.o
|
|
diff --git a/u-boot/drivers/video/sunxi_display2.c b/u-boot/drivers/video/sunxi_display2.c
|
|
new file mode 100644
|
|
index 0000000..ecef9f6
|
|
--- /dev/null
|
|
+++ b/u-boot/drivers/video/sunxi_display2.c
|
|
@@ -0,0 +1,1278 @@
|
|
+/*
|
|
+ * Display driver for sunxi Allwinner SoCs with DE2.
|
|
+ *
|
|
+ * Copyright (C) 2016 Jernej Skrabec <jernej.skrabec@siol.net>
|
|
+ *
|
|
+ * Based on sunxi_display.c:
|
|
+ * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
|
|
+ * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
|
|
+ *
|
|
+ * Based on Linux DRM driver:
|
|
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
|
|
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
|
|
+ *
|
|
+ * Based on rk_hdmi.c:
|
|
+ * Copyright (c) 2015 Google, Inc
|
|
+ * Copyright 2014 Rockchip Inc.
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+
|
|
+#include <common.h>
|
|
+
|
|
+#include <asm/arch/clock.h>
|
|
+#include <asm/arch/display2.h>
|
|
+#include <asm/global_data.h>
|
|
+#include <asm/io.h>
|
|
+#include <errno.h>
|
|
+#include <fdt_support.h>
|
|
+#include <video_fb.h>
|
|
+#include "videomodes.h"
|
|
+
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
+
|
|
+enum sunxi_monitor {
|
|
+ sunxi_monitor_none,
|
|
+ sunxi_monitor_dvi,
|
|
+ sunxi_monitor_hdmi,
|
|
+ sunxi_monitor_composite_pal,
|
|
+ sunxi_monitor_composite_ntsc,
|
|
+};
|
|
+#define SUNXI_MONITOR_LAST sunxi_monitor_hdmi
|
|
+
|
|
+struct sunxi_display {
|
|
+ GraphicDevice graphic_device;
|
|
+ enum sunxi_monitor monitor;
|
|
+ unsigned int depth;
|
|
+ unsigned int fb_addr;
|
|
+ unsigned int fb_size;
|
|
+} sunxi_display;
|
|
+
|
|
+const struct ctfb_res_modes composite_video_modes[2] = {
|
|
+ /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
|
|
+ { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
|
|
+ { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
|
|
+};
|
|
+
|
|
+static bool sunxi_is_composite(void)
|
|
+{
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_none:
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+ return false;
|
|
+ case sunxi_monitor_composite_pal:
|
|
+ case sunxi_monitor_composite_ntsc:
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false; /* Never reached */
|
|
+}
|
|
+
|
|
+static bool sunxi_is_hdmi(void)
|
|
+{
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_none:
|
|
+ case sunxi_monitor_composite_pal:
|
|
+ case sunxi_monitor_composite_ntsc:
|
|
+ return false;
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false; /* Never reached */
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+
|
|
+static void sunxi_hdmi_phy_init(void)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ unsigned long tmo;
|
|
+ u32 tmp;
|
|
+
|
|
+ /*
|
|
+ * HDMI PHY settings are taken as-is from Allwinner BSP code.
|
|
+ * There is no documentation.
|
|
+ */
|
|
+ writel(0, &hdmi->phy_ctrl);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(0));
|
|
+ udelay(5);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(16));
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(1));
|
|
+ udelay(10);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(2));
|
|
+ udelay(5);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(3));
|
|
+ udelay(40);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(19));
|
|
+ udelay(100);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(18));
|
|
+ setbits_le32(&hdmi->phy_ctrl, 7 << 4);
|
|
+
|
|
+ /* Note that Allwinner code doesn't fail in case of timeout */
|
|
+ tmo = timer_get_us() + 2000;
|
|
+ while ((readl(&hdmi->phy_status) & 0x80) == 0) {
|
|
+ if (timer_get_us() > tmo) {
|
|
+ printf("Warning: HDMI PHY init timeout!\n");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ setbits_le32(&hdmi->phy_ctrl, 0xf << 8);
|
|
+ setbits_le32(&hdmi->phy_ctrl, BIT(7));
|
|
+
|
|
+ writel(0x39dc5040, &hdmi->phy_pll);
|
|
+ writel(0x80084343, &hdmi->phy_clk);
|
|
+ udelay(10000);
|
|
+ writel(1, &hdmi->phy_unk3);
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(25));
|
|
+ udelay(100000);
|
|
+ tmp = (readl(&hdmi->phy_status) & 0x1f800) >> 11;
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(31) | BIT(30));
|
|
+ setbits_le32(&hdmi->phy_pll, tmp);
|
|
+ writel(0x01FF0F7F, &hdmi->phy_ctrl);
|
|
+ writel(0x80639000, &hdmi->phy_unk1);
|
|
+ writel(0x0F81C405, &hdmi->phy_unk2);
|
|
+
|
|
+ /* enable read access to HDMI controller */
|
|
+ writel(0x54524545, &hdmi->phy_read_en);
|
|
+ /* descramble register offsets */
|
|
+ writel(0x42494E47, &hdmi->phy_unscramble);
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_ctrl_init(void)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+
|
|
+ /* soft reset HDMI controller */
|
|
+ writeb(0x00, &hdmi->mc_swrstz);
|
|
+
|
|
+ udelay(1);
|
|
+
|
|
+ writeb(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
|
|
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT,
|
|
+ &hdmi->ih_mute);
|
|
+ writeb(HDMI_I2CM_CTLINT_ADDR_NACK_POL |
|
|
+ HDMI_I2CM_CTLINT_ADDR_NACK_MSK |
|
|
+ HDMI_I2CM_CTLINT_ADDR_ARB_POL |
|
|
+ HDMI_I2CM_CTLINT_ADDR_ARB_MSK,
|
|
+ &hdmi->i2cm_ctlint);
|
|
+ writeb(0xff & ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE,
|
|
+ &hdmi->mc_clkdis);
|
|
+}
|
|
+
|
|
+static int sunxi_hdmi_hpd_detect(int hpd_delay)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ unsigned long tmo = timer_get_us() + hpd_delay * 1000;
|
|
+ int status = 0;
|
|
+
|
|
+ /* Set pll3 to 297 MHz */
|
|
+ clock_set_pll3(297000000);
|
|
+
|
|
+ /* Set hdmi parent to pll3 */
|
|
+ clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
|
|
+ CCM_HDMI_CTRL_PLL3);
|
|
+
|
|
+ /* Set ahb gating to pass */
|
|
+ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
|
|
+ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2);
|
|
+ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
|
|
+ setbits_le32(&ccm->hdmi_slow_clk_cfg, CCM_HDMI_SLOW_CTRL_DDC_GATE);
|
|
+
|
|
+ /* Clock on */
|
|
+ setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
|
|
+
|
|
+ sunxi_hdmi_phy_init();
|
|
+ sunxi_hdmi_ctrl_init();
|
|
+
|
|
+ while (timer_get_us() < tmo) {
|
|
+ if (readl(&hdmi->phy_status) & SUNXI_HDMI_HPD_DETECT) {
|
|
+ status = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_shutdown(void)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+
|
|
+ writel(0, &hdmi->phy_ctrl);
|
|
+ clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
|
|
+ clrbits_le32(&ccm->hdmi_slow_clk_cfg, CCM_HDMI_SLOW_CTRL_DDC_GATE);
|
|
+ clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
|
|
+ clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
|
|
+ clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2);
|
|
+ clock_set_pll3(0);
|
|
+}
|
|
+
|
|
+static int sunxi_hdmi_ddc_wait_i2c_done(int msec)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ u32 val;
|
|
+ ulong start;
|
|
+
|
|
+ start = get_timer(0);
|
|
+ do {
|
|
+ val = readb(&hdmi->ih_i2cm_stat0);
|
|
+ writeb(val, &hdmi->ih_i2cm_stat0);
|
|
+
|
|
+ if (val & 0x2)
|
|
+ return 0;
|
|
+ if (val & 0x1)
|
|
+ return -EIO;
|
|
+
|
|
+ udelay(100);
|
|
+ } while (get_timer(start) < msec);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int sunxi_hdmi_ddc_read(int block, u8 *buf)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ int shift = (block % 2) * 0x80;
|
|
+ int trytime = 5;
|
|
+ int edid_read_err = 0;
|
|
+ u32 op = (block == 0) ? 1 : 2;
|
|
+ int n;
|
|
+
|
|
+ writeb(block >> 1, &hdmi->i2cm_segptr);
|
|
+
|
|
+ while (trytime--) {
|
|
+ edid_read_err = 0;
|
|
+
|
|
+ for (n = 0; n < 128; n++) {
|
|
+ writeb(shift + n, &hdmi->i2c_address);
|
|
+ writeb(op, &hdmi->i2cm_operation);
|
|
+
|
|
+ if (sunxi_hdmi_ddc_wait_i2c_done(10)) {
|
|
+ edid_read_err = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ *buf++ = readb(&hdmi->i2cm_datai);
|
|
+ }
|
|
+
|
|
+ if (!edid_read_err)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return edid_read_err;
|
|
+}
|
|
+
|
|
+static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
|
|
+{
|
|
+ int r, retries = 2;
|
|
+
|
|
+ do {
|
|
+ r = sunxi_hdmi_ddc_read(block, buf);
|
|
+ if (r)
|
|
+ continue;
|
|
+ r = edid_check_checksum(buf);
|
|
+ if (r) {
|
|
+ printf("EDID block %d: checksum error%s\n",
|
|
+ block, retries ? ", retrying" : "");
|
|
+ }
|
|
+ } while (r && retries--);
|
|
+
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ struct edid1_info edid1;
|
|
+ struct edid_cea861_info cea681[4];
|
|
+ struct edid_detailed_timing *t =
|
|
+ (struct edid_detailed_timing *)edid1.monitor_details.timing;
|
|
+ int i, r, ext_blocks = 0;
|
|
+
|
|
+ /* Reset i2c controller */
|
|
+ writeb(0, &hdmi->i2cm_softrstz);
|
|
+
|
|
+ writeb(0x05, &hdmi->i2cm_div);
|
|
+ writeb(0x08, &hdmi->i2cm_int);
|
|
+
|
|
+ /* set DDC timing*/
|
|
+ writeb(0xd8, &hdmi->i2cm_ss_scl_hcnt_0_addr);
|
|
+ writeb(0xfe, &hdmi->i2cm_ss_scl_lcnt_0_addr);
|
|
+
|
|
+ writeb(HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->i2cm_slave);
|
|
+ writeb(HMDI_DDC_ADDR_SEG_ADDR, &hdmi->i2cm_segaddr);
|
|
+
|
|
+ r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
|
|
+ if (r == 0) {
|
|
+ r = edid_check_info(&edid1);
|
|
+ if (r) {
|
|
+ printf("EDID: invalid EDID data\n");
|
|
+ r = -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ if (r == 0) {
|
|
+ ext_blocks = edid1.extension_flag;
|
|
+ if (ext_blocks > 4)
|
|
+ ext_blocks = 4;
|
|
+ for (i = 0; i < ext_blocks; i++) {
|
|
+ if (sunxi_hdmi_edid_get_block(1 + i,
|
|
+ (u8 *)&cea681[i]) != 0) {
|
|
+ ext_blocks = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (r)
|
|
+ return r;
|
|
+
|
|
+ /* We want version 1.3 or 1.2 with detailed timing info */
|
|
+ if (edid1.version != 1 || (edid1.revision < 3 &&
|
|
+ !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
|
|
+ printf("EDID: unsupported version %d.%d\n",
|
|
+ edid1.version, edid1.revision);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Take the first usable detailed timing */
|
|
+ for (i = 0; i < 4; i++, t++) {
|
|
+ r = video_edid_dtd_to_ctfb_res_modes(t, mode);
|
|
+ if (r == 0)
|
|
+ break;
|
|
+ }
|
|
+ if (i == 4) {
|
|
+ printf("EDID: no usable detailed timing found\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ /* Check for basic audio support, if found enable hdmi output */
|
|
+ sunxi_display.monitor = sunxi_monitor_dvi;
|
|
+ for (i = 0; i < ext_blocks; i++) {
|
|
+ if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
|
|
+ cea681[i].revision < 2)
|
|
+ continue;
|
|
+
|
|
+ if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
|
|
+ sunxi_display.monitor = sunxi_monitor_hdmi;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_VIDEO_HDMI */
|
|
+
|
|
+/*
|
|
+ * This is the entity that mixes and matches the different layers and inputs.
|
|
+ * Allwinner calls it display engine, but here is called composer.
|
|
+ */
|
|
+static void sunxi_composer_init(void)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+#if defined(CONFIG_MACH_SUN50I) && !defined(CONFIG_MACH_SUN50I_H5)
|
|
+ u32 reg_value;
|
|
+
|
|
+ /* set SRAM for video use (A64 only) */
|
|
+ reg_value = readl(SUNXI_SRAMC_BASE + 0x04);
|
|
+ reg_value &= ~(0x01 << 24);
|
|
+ writel(reg_value, SUNXI_SRAMC_BASE + 0x04);
|
|
+#endif
|
|
+
|
|
+ clock_set_pll10(432000000);
|
|
+
|
|
+ /* Set DE parent to pll10 */
|
|
+ clrsetbits_le32(&ccm->de_clk_cfg, CCM_DE2_CTRL_PLL_MASK,
|
|
+ CCM_DE2_CTRL_PLL10);
|
|
+
|
|
+ /* Set ahb gating to pass */
|
|
+ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE);
|
|
+ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE);
|
|
+
|
|
+ /* Clock on */
|
|
+ setbits_le32(&ccm->de_clk_cfg, CCM_DE2_CTRL_GATE);
|
|
+}
|
|
+
|
|
+static void sunxi_composer_mode_set(int mux, const struct ctfb_res_modes *mode,
|
|
+ unsigned int address)
|
|
+{
|
|
+ void *de_mux_base = (mux == 0) ? SUNXI_DE2_MUX0_BASE : SUNXI_DE2_MUX1_BASE;
|
|
+ struct de_clk * const de_clk_regs =
|
|
+ (struct de_clk *)(SUNXI_DE2_BASE);
|
|
+ struct de_glb * const de_glb_regs =
|
|
+ (struct de_glb *)(de_mux_base +
|
|
+ SUNXI_DE2_MUX_GLB_REGS);
|
|
+ struct de_bld * const de_bld_regs =
|
|
+ (struct de_bld *)(de_mux_base +
|
|
+ SUNXI_DE2_MUX_BLD_REGS);
|
|
+ struct de_ui * const de_ui_regs =
|
|
+ (struct de_ui *)(de_mux_base +
|
|
+ SUNXI_DE2_MUX_CHAN_REGS +
|
|
+ SUNXI_DE2_MUX_CHAN_SZ * 1);
|
|
+ struct de_csc * const de_csc_regs =
|
|
+ (struct de_csc *)(de_mux_base +
|
|
+ SUNXI_DE2_MUX_DCSC_REGS);
|
|
+ u32 size = SUNXI_DE2_WH(mode->xres, mode->yres);
|
|
+ int channel;
|
|
+ u32 data;
|
|
+
|
|
+ /* enable clock */
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) && !defined(CONFIG_MACH_SUN50I_H5)
|
|
+ setbits_le32(&de_clk_regs->rst_cfg, (mux == 0) ? 1 : 4);
|
|
+#else
|
|
+ setbits_le32(&de_clk_regs->rst_cfg, (mux == 0) ? 1 : 2);
|
|
+#endif
|
|
+ setbits_le32(&de_clk_regs->gate_cfg, (mux == 0) ? 1 : 2);
|
|
+ setbits_le32(&de_clk_regs->bus_cfg, (mux == 0) ? 1 : 2);
|
|
+
|
|
+ clrbits_le32(&de_clk_regs->sel_cfg, 1);
|
|
+
|
|
+ writel(SUNXI_DE2_MUX_GLB_CTL_RT_EN, &de_glb_regs->ctl);
|
|
+ writel(0, &de_glb_regs->status);
|
|
+ writel(1, &de_glb_regs->dbuff);
|
|
+ writel(size, &de_glb_regs->size);
|
|
+
|
|
+ for (channel = 0; channel < 4; channel++) {
|
|
+ void *chan = de_mux_base + SUNXI_DE2_MUX_CHAN_REGS +
|
|
+ SUNXI_DE2_MUX_CHAN_SZ * channel;
|
|
+ memset(chan, 0, channel == 0 ?
|
|
+ sizeof(struct de_vi) : sizeof(struct de_ui));
|
|
+ }
|
|
+ memset(de_bld_regs, 0, sizeof(struct de_bld));
|
|
+
|
|
+ writel(0x00000101, &de_bld_regs->fcolor_ctl);
|
|
+
|
|
+ writel(1, &de_bld_regs->route);
|
|
+
|
|
+ writel(0, &de_bld_regs->premultiply);
|
|
+ writel(0xff000000, &de_bld_regs->bkcolor);
|
|
+
|
|
+ writel(0x03010301, &de_bld_regs->bld_mode[0]);
|
|
+
|
|
+ writel(size, &de_bld_regs->output_size);
|
|
+ writel(mode->vmode & FB_VMODE_INTERLACED ? 2 : 0,
|
|
+ &de_bld_regs->out_ctl);
|
|
+ writel(0, &de_bld_regs->ck_ctl);
|
|
+
|
|
+ writel(0xff000000, &de_bld_regs->attr[0].fcolor);
|
|
+ writel(size, &de_bld_regs->attr[0].insize);
|
|
+
|
|
+ /* Disable all other units */
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_VSU_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_GSU1_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_GSU2_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_GSU3_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_FCE_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_BWS_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_LTI_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_PEAK_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_ASE_REGS);
|
|
+ writel(0, de_mux_base + SUNXI_DE2_MUX_FCC_REGS);
|
|
+
|
|
+ if (sunxi_is_composite()) {
|
|
+ writel(0x107, &de_csc_regs->coef11);
|
|
+ writel(0x204, &de_csc_regs->coef12);
|
|
+ writel(0x64, &de_csc_regs->coef13);
|
|
+ writel(0x4200, &de_csc_regs->coef14);
|
|
+ writel(0x1f68, &de_csc_regs->coef21);
|
|
+ writel(0x1ed6, &de_csc_regs->coef22);
|
|
+ writel(0x1c2, &de_csc_regs->coef23);
|
|
+ writel(0x20200, &de_csc_regs->coef24);
|
|
+ writel(0x1c2, &de_csc_regs->coef31);
|
|
+ writel(0x1e87, &de_csc_regs->coef32);
|
|
+ writel(0x1fb7, &de_csc_regs->coef33);
|
|
+ writel(0x20200, &de_csc_regs->coef34);
|
|
+ writel(0x20200, &de_csc_regs->alpha);
|
|
+
|
|
+ writel(1, &de_csc_regs->csc_ctl);
|
|
+ } else {
|
|
+ writel(0, &de_csc_regs->csc_ctl);
|
|
+ }
|
|
+
|
|
+ data = SUNXI_DE2_UI_CFG_ATTR_EN |
|
|
+ SUNXI_DE2_UI_CFG_ATTR_FMT(SUNXI_DE2_FORMAT_XRGB_8888) |
|
|
+ SUNXI_DE2_UI_CFG_ATTR_ALPMOD(1) |
|
|
+ SUNXI_DE2_UI_CFG_ATTR_ALPHA(0xff);
|
|
+ writel(data, &de_ui_regs->cfg[0].attr);
|
|
+ writel(size, &de_ui_regs->cfg[0].size);
|
|
+ writel(0, &de_ui_regs->cfg[0].coord);
|
|
+ writel(4 * mode->xres, &de_ui_regs->cfg[0].pitch);
|
|
+ writel(address, &de_ui_regs->cfg[0].top_laddr);
|
|
+ writel(size, &de_ui_regs->ovl_size);
|
|
+}
|
|
+
|
|
+static void sunxi_composer_enable(int mux)
|
|
+{
|
|
+ struct de_glb *de_glb_regs;
|
|
+
|
|
+ if (mux == 0)
|
|
+ de_glb_regs = (struct de_glb *)(SUNXI_DE2_MUX0_BASE +
|
|
+ SUNXI_DE2_MUX_GLB_REGS);
|
|
+ else
|
|
+ de_glb_regs = (struct de_glb *)(SUNXI_DE2_MUX1_BASE +
|
|
+ SUNXI_DE2_MUX_GLB_REGS);
|
|
+
|
|
+ writel(1, &de_glb_regs->dbuff);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
|
|
+ */
|
|
+static void sunxi_lcdc_pll_set(int dotclock, int *clk_div)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ int value, n, m, x = 0, diff;
|
|
+ int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
|
|
+
|
|
+ /*
|
|
+ * Due to unknown registers in HDMI PHY, we know correct settings
|
|
+ * only for following four PHY dividers. Select one based on
|
|
+ * clock speed.
|
|
+ */
|
|
+ if (dotclock <= 27000)
|
|
+ x = 11;
|
|
+ else if (dotclock <= 74250)
|
|
+ x = 4;
|
|
+ else if (dotclock <= 148500)
|
|
+ x = 2;
|
|
+ else
|
|
+ x = 1;
|
|
+
|
|
+ /*
|
|
+ * Find the lowest divider resulting in a matching clock. If there
|
|
+ * is no match, pick the closest lower clock, as monitors tend to
|
|
+ * not sync to higher frequencies.
|
|
+ */
|
|
+ for (m = 1; m <= 16; m++) {
|
|
+ n = (m * x * dotclock) / 24000;
|
|
+
|
|
+ if ((n >= 1) && (n <= 128)) {
|
|
+ value = (24000 * n) / m / x;
|
|
+ diff = dotclock - value;
|
|
+ if (diff < best_diff) {
|
|
+ best_diff = diff;
|
|
+ best_m = m;
|
|
+ best_n = n;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ clock_set_pll3_factors(best_m, best_n);
|
|
+ debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
|
|
+ dotclock, (clock_get_pll3() / 1000) / x,
|
|
+ best_n, best_m, x);
|
|
+
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) || defined(CONFIG_MACH_SUN50I_H5)
|
|
+ writel(CCM_TCON0_CTRL_GATE | CCM_TCON0_CTRL_M(x),
|
|
+ &ccm->tcon0_clk_cfg);
|
|
+#else
|
|
+ writel(CCM_TCON1_CTRL_GATE | CCM_TCON1_CTRL_M(x),
|
|
+ &ccm->tcon1_clk_cfg);
|
|
+#endif
|
|
+
|
|
+ *clk_div = x;
|
|
+}
|
|
+
|
|
+static void sunxi_lcdc_init(int num)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ struct sunxi_lcdc_reg *lcdc;
|
|
+
|
|
+ if (num == 0) {
|
|
+ lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
|
|
+
|
|
+ /* Reset off */
|
|
+ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_TCON0);
|
|
+
|
|
+ /* Clock on */
|
|
+ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TCON0);
|
|
+ setbits_le32(&ccm->tcon0_clk_cfg, CCM_TCON0_CTRL_GATE);
|
|
+ } else {
|
|
+ lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
|
|
+
|
|
+ /* Reset off */
|
|
+ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_TCON1);
|
|
+
|
|
+ /* Clock on */
|
|
+ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TCON1);
|
|
+#if !defined(CONFIG_MACH_SUN50I_H5) && defined(CONFIG_MACH_SUN50I)
|
|
+ setbits_le32(&ccm->tcon1_clk_cfg, CCM_TCON1_CTRL_GATE);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ /* Init lcdc */
|
|
+ writel(0, &lcdc->ctrl); /* Disable tcon */
|
|
+ writel(0, &lcdc->int0); /* Disable all interrupts */
|
|
+
|
|
+ /* Set all io lines to tristate */
|
|
+ writel(0x0fffffff, &lcdc->tcon1_io_tristate);
|
|
+}
|
|
+
|
|
+static void sunxi_lcdc_enable(int num)
|
|
+{
|
|
+ struct sunxi_lcdc_reg *lcdc;
|
|
+
|
|
+ if (num == 0)
|
|
+ lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
|
|
+ else
|
|
+ lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
|
|
+
|
|
+ setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
|
|
+}
|
|
+
|
|
+static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode)
|
|
+{
|
|
+ int delay;
|
|
+
|
|
+ delay = mode->lower_margin + mode->vsync_len + mode->upper_margin;
|
|
+ if (mode->vmode == FB_VMODE_INTERLACED)
|
|
+ delay /= 2;
|
|
+ delay -= 2;
|
|
+
|
|
+ return (delay > 31) ? 31 : delay;
|
|
+}
|
|
+
|
|
+#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_COMPOSITE
|
|
+static void sunxi_lcdc_tcon1_mode_set(int num,
|
|
+ const struct ctfb_res_modes *mode,
|
|
+ int *clk_div)
|
|
+{
|
|
+ struct sunxi_lcdc_reg *lcdc;
|
|
+ int bp, clk_delay, total, yres;
|
|
+
|
|
+ if (num == 0)
|
|
+ lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
|
|
+ else
|
|
+ lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
|
|
+
|
|
+ clk_delay = sunxi_lcdc_get_clk_delay(mode);
|
|
+ writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
|
|
+ ((mode->vmode == FB_VMODE_INTERLACED) ?
|
|
+ SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) |
|
|
+ SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
|
|
+
|
|
+ yres = mode->yres;
|
|
+ if (mode->vmode == FB_VMODE_INTERLACED)
|
|
+ yres /= 2;
|
|
+ writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
|
|
+ &lcdc->tcon1_timing_source);
|
|
+ writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
|
|
+ &lcdc->tcon1_timing_scale);
|
|
+ writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
|
|
+ &lcdc->tcon1_timing_out);
|
|
+
|
|
+ bp = mode->hsync_len + mode->left_margin;
|
|
+ total = mode->xres + mode->right_margin + bp;
|
|
+ writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
|
|
+ SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
|
|
+
|
|
+ bp = mode->vsync_len + mode->upper_margin;
|
|
+ total = mode->yres + mode->lower_margin + bp;
|
|
+ if (mode->vmode == FB_VMODE_NONINTERLACED)
|
|
+ total *= 2;
|
|
+ writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
|
|
+ SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
|
|
+
|
|
+ writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
|
|
+ &lcdc->tcon1_timing_sync);
|
|
+
|
|
+ if (!sunxi_is_composite())
|
|
+ sunxi_lcdc_pll_set(mode->pixclock_khz, clk_div);
|
|
+}
|
|
+#endif /* CONFIG_VIDEO_HDMI || CONFIG_VIDEO_COMPOSITE */
|
|
+
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+
|
|
+static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ u8 tmp;
|
|
+
|
|
+ if (mode->pixclock_khz <= 27000)
|
|
+ tmp = 0x40; /* SD-modes, ITU601 colorspace */
|
|
+ else
|
|
+ tmp = 0x80; /* HD-modes, ITU709 colorspace */
|
|
+
|
|
+ if (mode->xres * 100 / mode->yres < 156)
|
|
+ tmp |= 0x18; /* 4 : 3 */
|
|
+ else
|
|
+ tmp |= 0x28; /* 16 : 9 */
|
|
+
|
|
+ setbits_8(&hdmi->fc_invidconf,
|
|
+ HDMI_FC_INVIDCONF_DVI_MODE_HDMI);
|
|
+ writeb(HDMI_FC_AVICONF0_ACTIVE_FORMAT |
|
|
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN,
|
|
+ &hdmi->fc_aviconf0);
|
|
+ writeb(tmp, &hdmi->fc_aviconf1);
|
|
+ writeb(HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE |
|
|
+ HDMI_FC_AVICONF2_IT_CONTENT_VALID,
|
|
+ &hdmi->fc_aviconf2);
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_phy_set(u32 divider)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ u32 tmp;
|
|
+
|
|
+ /*
|
|
+ * Unfortunatelly, we don't know much about those magic
|
|
+ * numbers. They are taken from Allwinner BSP driver.
|
|
+ */
|
|
+ switch (divider) {
|
|
+ case 1:
|
|
+ writel(0x30dc5fc0, &hdmi->phy_pll);
|
|
+ writel(0x800863C0, &hdmi->phy_clk);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, &hdmi->phy_unk3);
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(25));
|
|
+ mdelay(200);
|
|
+ tmp = (readl(&hdmi->phy_status) & 0x1f800) >> 11;
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(31) | BIT(30));
|
|
+ if (tmp < 0x3d)
|
|
+ setbits_le32(&hdmi->phy_pll, tmp + 2);
|
|
+ else
|
|
+ setbits_le32(&hdmi->phy_pll, 0x3f);
|
|
+ mdelay(100);
|
|
+ writel(0x01FFFF7F, &hdmi->phy_ctrl);
|
|
+ writel(0x8063b000, &hdmi->phy_unk1);
|
|
+ writel(0x0F8246B5, &hdmi->phy_unk2);
|
|
+ break;
|
|
+ case 2:
|
|
+ writel(0x39dc5040, &hdmi->phy_pll);
|
|
+ writel(0x80084381, &hdmi->phy_clk);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, &hdmi->phy_unk3);
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(25));
|
|
+ mdelay(100);
|
|
+ tmp = (readl(&hdmi->phy_status) & 0x1f800) >> 11;
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(31) | BIT(30));
|
|
+ setbits_le32(&hdmi->phy_pll, tmp);
|
|
+ writel(0x01FFFF7F, &hdmi->phy_ctrl);
|
|
+ writel(0x8063a800, &hdmi->phy_unk1);
|
|
+ writel(0x0F81C485, &hdmi->phy_unk2);
|
|
+ break;
|
|
+ case 4:
|
|
+ writel(0x39dc5040, &hdmi->phy_pll);
|
|
+ writel(0x80084343, &hdmi->phy_clk);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, &hdmi->phy_unk3);
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(25));
|
|
+ mdelay(100);
|
|
+ tmp = (readl(&hdmi->phy_status) & 0x1f800) >> 11;
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(31) | BIT(30));
|
|
+ setbits_le32(&hdmi->phy_pll, tmp);
|
|
+ writel(0x01FFFF7F, &hdmi->phy_ctrl);
|
|
+ writel(0x8063b000, &hdmi->phy_unk1);
|
|
+ writel(0x0F81C405, &hdmi->phy_unk2);
|
|
+ break;
|
|
+ case 11:
|
|
+ writel(0x39dc5040, &hdmi->phy_pll);
|
|
+ writel(0x8008430a, &hdmi->phy_clk);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, &hdmi->phy_unk3);
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(25));
|
|
+ mdelay(100);
|
|
+ tmp = (readl(&hdmi->phy_status) & 0x1f800) >> 11;
|
|
+ setbits_le32(&hdmi->phy_pll, BIT(31) | BIT(30));
|
|
+ setbits_le32(&hdmi->phy_pll, tmp);
|
|
+ writel(0x01FFFF7F, &hdmi->phy_ctrl);
|
|
+ writel(0x8063b000, &hdmi->phy_unk1);
|
|
+ writel(0x0F81C405, &hdmi->phy_unk2);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
|
|
+ int clk_div)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+ u8 invidconf, v_blanking;
|
|
+ u32 h_blanking;
|
|
+
|
|
+ sunxi_hdmi_phy_set(clk_div);
|
|
+
|
|
+ invidconf = 0;
|
|
+ if (mode->vmode & FB_VMODE_INTERLACED)
|
|
+ invidconf |= 0x01;
|
|
+ if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
|
|
+ invidconf |= 0x20;
|
|
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
|
|
+ invidconf |= 0x40;
|
|
+
|
|
+ h_blanking = mode->left_margin + mode->right_margin + mode->hsync_len;
|
|
+ v_blanking = mode->upper_margin + mode->lower_margin + mode->vsync_len;
|
|
+
|
|
+ writeb(invidconf |
|
|
+ HDMI_FC_INVIDCONF_DE_IN_POL_ACTIVE_HIGH,
|
|
+ &hdmi->fc_invidconf);
|
|
+ if (invidconf < 96)
|
|
+ setbits_le32(&hdmi->phy_pol, 0x300);
|
|
+
|
|
+ writeb(mode->xres, &hdmi->fc_inhactv0);
|
|
+ writeb(mode->xres >> 8, &hdmi->fc_inhactv1);
|
|
+ writeb(h_blanking, &hdmi->fc_inhblank0);
|
|
+ writeb(h_blanking >> 8, &hdmi->fc_inhblank1);
|
|
+ writeb(mode->yres, &hdmi->fc_invactv0);
|
|
+ writeb(mode->yres >> 8, &hdmi->fc_invactv1);
|
|
+ writeb(v_blanking, &hdmi->fc_invblank);
|
|
+ writeb(mode->right_margin, &hdmi->fc_hsyncindelay0);
|
|
+ writeb(mode->right_margin >> 8, &hdmi->fc_hsyncindelay1);
|
|
+ writeb(mode->hsync_len, &hdmi->fc_hsyncinwidth0);
|
|
+ writeb(mode->hsync_len >> 8, &hdmi->fc_hsyncinwidth1);
|
|
+ writeb(mode->lower_margin, &hdmi->fc_vsyncindelay);
|
|
+ writeb(mode->vsync_len, &hdmi->fc_vsyncinwidth);
|
|
+
|
|
+ /* control period minimum duration */
|
|
+ writeb(0x0c, &hdmi->fc_ctrldur);
|
|
+ writeb(0x20, &hdmi->fc_exctrldur);
|
|
+ writeb(0x01, &hdmi->fc_exctrlspac);
|
|
+
|
|
+ /* set to fill tmds data channels */
|
|
+ writeb(0x0b, &hdmi->fc_ch0pream);
|
|
+ writeb(0x16, &hdmi->fc_ch1pream);
|
|
+ writeb(0x21, &hdmi->fc_ch2pream);
|
|
+
|
|
+ writeb(0x40, &hdmi->vp_pr_cd);
|
|
+ writeb(0x07, &hdmi->vp_stuff);
|
|
+ writeb(0x00, &hdmi->vp_remap);
|
|
+ writeb(0x47, &hdmi->vp_conf);
|
|
+
|
|
+ writeb(0x01, &hdmi->tx_invid0);
|
|
+
|
|
+ /* enable tx stuffing: when de is inactive, fix the output data to 0 */
|
|
+ writeb(HDMI_TX_INSTUFFING_BDBDATA_STUFFING_EN |
|
|
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_EN |
|
|
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_EN,
|
|
+ &hdmi->tx_instuffing);
|
|
+ writeb(0x00, &hdmi->tx_gydata0);
|
|
+ writeb(0x00, &hdmi->tx_gydata1);
|
|
+ writeb(0x00, &hdmi->tx_rcrdata0);
|
|
+ writeb(0x00, &hdmi->tx_rcrdata1);
|
|
+ writeb(0x00, &hdmi->tx_bcbdata0);
|
|
+ writeb(0x00, &hdmi->tx_bcbdata1);
|
|
+
|
|
+ if (sunxi_display.monitor == sunxi_monitor_hdmi)
|
|
+ sunxi_hdmi_setup_info_frames(mode);
|
|
+
|
|
+ writeb(HDMI_MC_FLOWCTRL_CSC_BYPASS, &hdmi->mc_flowctrl);
|
|
+ /* enable audio, TMDS and pixel clock */
|
|
+ writeb(0x74, &hdmi->mc_clkdis);
|
|
+
|
|
+ /*
|
|
+ * This is last hdmi access before boot,
|
|
+ * so scramble addresses again. Othwerwise
|
|
+ * BSP or current DRM driver won't work.
|
|
+ * Dummy read is needed or otherwise last
|
|
+ * write doesn't get written correctly.
|
|
+ */
|
|
+ (void)readb(&hdmi->reserved0[0]);
|
|
+ writel(0, &hdmi->phy_unscramble);
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_enable(void)
|
|
+{
|
|
+ struct sunxi_dwc_hdmi * const hdmi =
|
|
+ (struct sunxi_dwc_hdmi *)SUNXI_HDMI_BASE;
|
|
+
|
|
+ setbits_le32(&hdmi->phy_ctrl, 0xf << 12);
|
|
+ printf("hdmi enabled\n");
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_VIDEO_HDMI */
|
|
+
|
|
+#ifdef CONFIG_VIDEO_COMPOSITE
|
|
+
|
|
+static void sunxi_tvencoder_mode_set(void)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ struct sunxi_tve_reg * const tve =
|
|
+ (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
|
|
+
|
|
+ /* Reset off */
|
|
+ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_TVE);
|
|
+
|
|
+ /* Clock on */
|
|
+ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE);
|
|
+ writel(CCM_TVE_CTRL_GATE | CCM_TVE_CTRL_M(2), &ccm->tve_clk_cfg);
|
|
+
|
|
+#ifdef CONFIG_MACH_SUN50I_H5
|
|
+ writel(0x285 << 16, SUNXI_TVE0_BASE + 0x304);
|
|
+ writel(0x00101110, SUNXI_TVE0_BASE + 0x30c);
|
|
+#endif
|
|
+
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_composite_pal:
|
|
+ writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
|
|
+ SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
|
|
+ SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) |
|
|
+ SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl);
|
|
+ writel(SUNXI_TVE_CFG0_PAL, &tve->cfg0);
|
|
+ writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0);
|
|
+ writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter);
|
|
+ writel(0x2a098acb, &tve->chroma_freq);
|
|
+ writel(SUNXI_TVE_PORCH_NUM_PAL, &tve->porch_num);
|
|
+ writel(SUNXI_TVE_LINE_NUM_PAL, &tve->line_num);
|
|
+ writel(SUNXI_TVE_BLANK_BLACK_LEVEL_PAL, &tve->blank_black_level);
|
|
+ writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1);
|
|
+ writel(SUNXI_TVE_CBR_LEVEL_PAL, &tve->cbr_level);
|
|
+ writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width);
|
|
+ writel(SUNXI_TVE_UNKNOWN2_PAL, &tve->unknown2);
|
|
+ writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num);
|
|
+ writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain);
|
|
+ writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width);
|
|
+ writel(SUNXI_TVE_RESYNC_NUM_PAL, &tve->resync_num);
|
|
+ writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para);
|
|
+ break;
|
|
+ case sunxi_monitor_composite_ntsc:
|
|
+ writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
|
|
+ SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
|
|
+ SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) |
|
|
+ SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl);
|
|
+ writel(SUNXI_TVE_CFG0_NTSC, &tve->cfg0);
|
|
+ writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0);
|
|
+ writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter);
|
|
+ writel(SUNXI_TVE_PORCH_NUM_NTSC, &tve->porch_num);
|
|
+ writel(SUNXI_TVE_LINE_NUM_NTSC, &tve->line_num);
|
|
+ writel(SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC, &tve->blank_black_level);
|
|
+ writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1);
|
|
+ writel(SUNXI_TVE_CBR_LEVEL_NTSC, &tve->cbr_level);
|
|
+ writel(SUNXI_TVE_BURST_PHASE_NTSC, &tve->burst_phase);
|
|
+ writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width);
|
|
+ writel(SUNXI_TVE_UNKNOWN2_NTSC, &tve->unknown2);
|
|
+ writel(SUNXI_TVE_SYNC_VBI_LEVEL_NTSC, &tve->sync_vbi_level);
|
|
+ writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num);
|
|
+ writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain);
|
|
+ writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width);
|
|
+ writel(SUNXI_TVE_RESYNC_NUM_NTSC, &tve->resync_num);
|
|
+ writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para);
|
|
+ break;
|
|
+ case sunxi_monitor_none:
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void sunxi_tvencoder_enable(void)
|
|
+{
|
|
+ struct sunxi_tve_reg * const tve =
|
|
+ (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
|
|
+
|
|
+ setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_VIDEO_COMPOSITE */
|
|
+
|
|
+static void sunxi_engines_init(void)
|
|
+{
|
|
+#if defined(CONFIG_MACH_SUN50I_H5) || defined(CONFIG_MACH_SUN8I_H3)
|
|
+ int mux = sunxi_is_hdmi() ? 0 : 1;
|
|
+#else
|
|
+ int mux = sunxi_is_hdmi() ? 1 : 0;
|
|
+#endif
|
|
+ sunxi_composer_init();
|
|
+ sunxi_lcdc_init(mux);
|
|
+}
|
|
+
|
|
+static void sunxi_mode_set(const struct ctfb_res_modes *mode,
|
|
+ unsigned int address)
|
|
+{
|
|
+ int __maybe_unused clk_div;
|
|
+ int __maybe_unused mux;
|
|
+
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_none:
|
|
+ break;
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+#if defined(CONFIG_MACH_SUN50I_H5) || defined(CONFIG_MACH_SUN8I_H3)
|
|
+ mux = 0;
|
|
+#else
|
|
+ mux = 1;
|
|
+#endif
|
|
+ sunxi_composer_mode_set(mux, mode, address);
|
|
+ sunxi_lcdc_tcon1_mode_set(mux, mode, &clk_div);
|
|
+ sunxi_hdmi_mode_set(mode, clk_div);
|
|
+ sunxi_composer_enable(mux);
|
|
+ sunxi_lcdc_enable(mux);
|
|
+ sunxi_hdmi_enable();
|
|
+#endif
|
|
+ break;
|
|
+ case sunxi_monitor_composite_pal:
|
|
+ case sunxi_monitor_composite_ntsc:
|
|
+#ifdef CONFIG_VIDEO_COMPOSITE
|
|
+ sunxi_composer_mode_set(1, mode, address);
|
|
+ sunxi_lcdc_tcon1_mode_set(1, mode, &clk_div);
|
|
+ sunxi_tvencoder_mode_set();
|
|
+ sunxi_composer_enable(1);
|
|
+ sunxi_lcdc_enable(1);
|
|
+ sunxi_tvencoder_enable();
|
|
+#endif
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
|
|
+{
|
|
+ switch (monitor) {
|
|
+ case sunxi_monitor_none: return "none";
|
|
+ case sunxi_monitor_dvi: return "dvi";
|
|
+ case sunxi_monitor_hdmi: return "hdmi";
|
|
+ case sunxi_monitor_composite_pal: return "composite-pal";
|
|
+ case sunxi_monitor_composite_ntsc: return "composite-ntsc";
|
|
+ }
|
|
+ return NULL; /* never reached */
|
|
+}
|
|
+
|
|
+ulong board_get_usable_ram_top(ulong total_size)
|
|
+{
|
|
+ return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
|
|
+}
|
|
+
|
|
+static bool sunxi_has_hdmi(void)
|
|
+{
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+ return true;
|
|
+#else
|
|
+ return false;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static bool sunxi_has_composite(void)
|
|
+{
|
|
+#ifdef CONFIG_VIDEO_COMPOSITE
|
|
+ return true;
|
|
+#else
|
|
+ return false;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
|
|
+{
|
|
+ if (allow_hdmi && sunxi_has_hdmi())
|
|
+ return sunxi_monitor_dvi;
|
|
+ else if (sunxi_has_composite())
|
|
+ return sunxi_monitor_composite_pal;
|
|
+ else
|
|
+ return sunxi_monitor_none;
|
|
+}
|
|
+
|
|
+void *video_hw_init(void)
|
|
+{
|
|
+ static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
|
|
+ const struct ctfb_res_modes *mode;
|
|
+ struct ctfb_res_modes custom;
|
|
+ const char *options;
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+ int ret, hpd, hpd_delay, edid;
|
|
+#endif
|
|
+ int i, overscan_offset, overscan_x, overscan_y;
|
|
+ unsigned int fb_dma_addr;
|
|
+ char mon[16];
|
|
+
|
|
+ memset(&sunxi_display, 0, sizeof(struct sunxi_display));
|
|
+
|
|
+ video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
|
|
+ &sunxi_display.depth, &options);
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+ hpd = video_get_option_int(options, "hpd", 1);
|
|
+ hpd_delay = video_get_option_int(options, "hpd_delay", 500);
|
|
+ edid = video_get_option_int(options, "edid", 1);
|
|
+#endif
|
|
+ overscan_x = video_get_option_int(options, "overscan_x", -1);
|
|
+ overscan_y = video_get_option_int(options, "overscan_y", -1);
|
|
+ sunxi_display.monitor = sunxi_get_default_mon(true);
|
|
+ video_get_option_string(options, "monitor", mon, sizeof(mon),
|
|
+ sunxi_get_mon_desc(sunxi_display.monitor));
|
|
+ for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
|
|
+ if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
|
|
+ sunxi_display.monitor = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (i > SUNXI_MONITOR_LAST)
|
|
+ printf("Unknown monitor: '%s', falling back to '%s'\n",
|
|
+ mon, sunxi_get_mon_desc(sunxi_display.monitor));
|
|
+
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+ /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
|
|
+ if (sunxi_display.monitor == sunxi_monitor_dvi ||
|
|
+ sunxi_display.monitor == sunxi_monitor_hdmi) {
|
|
+ /* Always call hdp_detect, as it also enables clocks, etc. */
|
|
+ ret = sunxi_hdmi_hpd_detect(hpd_delay);
|
|
+ if (ret) {
|
|
+ printf("HDMI connected: ");
|
|
+ if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
|
|
+ mode = &custom;
|
|
+ } else if (hpd) {
|
|
+ sunxi_hdmi_shutdown();
|
|
+ sunxi_display.monitor = sunxi_get_default_mon(false);
|
|
+ } /* else continue with hdmi/dvi without a cable connected */
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_none:
|
|
+ return NULL;
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+ if (!sunxi_has_hdmi()) {
|
|
+ printf("HDMI/DVI not supported on this board\n");
|
|
+ sunxi_display.monitor = sunxi_monitor_none;
|
|
+ return NULL;
|
|
+ }
|
|
+ break;
|
|
+ case sunxi_monitor_composite_pal:
|
|
+ case sunxi_monitor_composite_ntsc:
|
|
+ if (!sunxi_has_composite()) {
|
|
+ printf("Composite video not supported on this board\n");
|
|
+ sunxi_display.monitor = sunxi_monitor_none;
|
|
+ return NULL;
|
|
+ }
|
|
+ if (sunxi_display.monitor == sunxi_monitor_composite_pal)
|
|
+ mode = &composite_video_modes[0];
|
|
+ else
|
|
+ mode = &composite_video_modes[1];
|
|
+ sunxi_display.depth = 24;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Yes these defaults are quite high, overscan on composite sucks... */
|
|
+ if (overscan_x == -1)
|
|
+ overscan_x = sunxi_is_composite() ? 32 : 0;
|
|
+ if (overscan_y == -1)
|
|
+ overscan_y = sunxi_is_composite() ? 20 : 0;
|
|
+
|
|
+ sunxi_display.fb_size =
|
|
+ (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
|
|
+ overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
|
|
+ /* We want to keep the fb_base for simplefb page aligned, where as
|
|
+ * the sunxi dma engines will happily accept an unaligned address. */
|
|
+ if (overscan_offset)
|
|
+ sunxi_display.fb_size += 0x1000;
|
|
+
|
|
+ if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
|
|
+ printf("Error need %dkB for fb, but only %dkB is reserved\n",
|
|
+ sunxi_display.fb_size >> 10,
|
|
+ CONFIG_SUNXI_MAX_FB_SIZE >> 10);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
|
|
+ mode->xres, mode->yres,
|
|
+ (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
|
|
+ sunxi_get_mon_desc(sunxi_display.monitor),
|
|
+ overscan_x, overscan_y);
|
|
+
|
|
+ gd->fb_base = gd->bd->bi_dram[0].start +
|
|
+ gd->bd->bi_dram[0].size - sunxi_display.fb_size;
|
|
+ sunxi_engines_init();
|
|
+
|
|
+ fb_dma_addr = gd->fb_base;
|
|
+ sunxi_display.fb_addr = gd->fb_base;
|
|
+ if (overscan_offset) {
|
|
+ fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
|
|
+ sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
|
|
+ memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
|
|
+ flush_cache(gd->fb_base, sunxi_display.fb_size);
|
|
+ }
|
|
+ sunxi_mode_set(mode, fb_dma_addr);
|
|
+
|
|
+ /*
|
|
+ * These are the only members of this structure that are used. All the
|
|
+ * others are driver specific. The pitch is stored in plnSizeX.
|
|
+ */
|
|
+ graphic_device->frameAdrs = sunxi_display.fb_addr;
|
|
+ graphic_device->gdfIndex = GDF_32BIT_X888RGB;
|
|
+ graphic_device->gdfBytesPP = 4;
|
|
+ graphic_device->winSizeX = mode->xres - 2 * overscan_x;
|
|
+ graphic_device->winSizeY = mode->yres - 2 * overscan_y;
|
|
+ graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
|
|
+
|
|
+ return graphic_device;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Simplefb support.
|
|
+ */
|
|
+#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
|
|
+int sunxi_simplefb_setup(void *blob)
|
|
+{
|
|
+ static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
|
|
+ int offset, ret;
|
|
+ u64 start, size;
|
|
+ const char *pipeline = NULL;
|
|
+
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_none:
|
|
+ return 0;
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+ pipeline = "de0-lcd0-hdmi";
|
|
+ break;
|
|
+ case sunxi_monitor_composite_pal:
|
|
+ case sunxi_monitor_composite_ntsc:
|
|
+ pipeline = "de0-lcd1-tve0";
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Find a prefilled simpefb node, matching out pipeline config */
|
|
+ offset = fdt_node_offset_by_compatible(blob, -1,
|
|
+ "allwinner,simple-framebuffer");
|
|
+ while (offset >= 0) {
|
|
+ ret = fdt_stringlist_search(blob, offset, "allwinner,pipeline",
|
|
+ pipeline);
|
|
+ if (ret == 0)
|
|
+ break;
|
|
+ offset = fdt_node_offset_by_compatible(blob, offset,
|
|
+ "allwinner,simple-framebuffer");
|
|
+ }
|
|
+ if (offset < 0) {
|
|
+ eprintf("Cannot setup simplefb: node not found\n");
|
|
+ return 0; /* Keep older kernels working */
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Do not report the framebuffer as free RAM to the OS, note we cannot
|
|
+ * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
|
|
+ * and e.g. Linux refuses to iomap RAM on ARM, see:
|
|
+ * linux/arch/arm/mm/ioremap.c around line 301.
|
|
+ */
|
|
+ start = gd->bd->bi_dram[0].start;
|
|
+ size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
|
|
+ ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
|
|
+ if (ret) {
|
|
+ eprintf("Cannot setup simplefb: Error reserving memory\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
|
|
+ graphic_device->winSizeX, graphic_device->winSizeY,
|
|
+ graphic_device->plnSizeX, "x8r8g8b8");
|
|
+ if (ret)
|
|
+ eprintf("Cannot setup simplefb: Error setting properties\n");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */
|
|
diff --git a/u-boot/include/configs/sunxi-common.h b/u-boot/include/configs/sunxi-common.h
|
|
index d48bdac..5919a4f 100644
|
|
--- a/u-boot/include/configs/sunxi-common.h
|
|
+++ b/u-boot/include/configs/sunxi-common.h
|
|
@@ -274,16 +274,23 @@ extern int soft_i2c_gpio_scl;
|
|
#define CONFIG_SUNXI_GPIO
|
|
|
|
#ifdef CONFIG_VIDEO
|
|
+/* Do we want to initialize a simple FB? */
|
|
+#define CONFIG_VIDEO_DT_SIMPLEFB
|
|
+#if defined(CONFIG_MACH_SUN8I_H3) || defined(CONFIG_MACH_SUN50I)
|
|
+#define CONFIG_VIDEO_SUNXI2
|
|
+#else
|
|
+#define CONFIG_VIDEO_SUNXI
|
|
+#endif
|
|
+
|
|
/*
|
|
* The amount of RAM to keep free at the top of RAM when relocating u-boot,
|
|
* to use as framebuffer. This must be a multiple of 4096.
|
|
*/
|
|
+#ifdef CONFIG_VIDEO_SUNXI2
|
|
+#define CONFIG_SUNXI_MAX_FB_SIZE (32 << 20)
|
|
+#else
|
|
#define CONFIG_SUNXI_MAX_FB_SIZE (16 << 20)
|
|
-
|
|
-/* Do we want to initialize a simple FB? */
|
|
-#define CONFIG_VIDEO_DT_SIMPLEFB
|
|
-
|
|
-#define CONFIG_VIDEO_SUNXI
|
|
+#endif
|
|
|
|
#define CONFIG_VIDEO_LOGO
|
|
#define CONFIG_VIDEO_STD_TIMINGS
|
|
diff --git a/u-boot/include/video_fb.h b/u-boot/include/video_fb.h
|
|
index b008853..4a4fd5f 100644
|
|
--- a/u-boot/include/video_fb.h
|
|
+++ b/u-boot/include/video_fb.h
|
|
@@ -38,7 +38,7 @@ typedef struct graphic_device {
|
|
unsigned int dprBase;
|
|
unsigned int vprBase;
|
|
unsigned int cprBase;
|
|
- unsigned int frameAdrs;
|
|
+ unsigned long frameAdrs;
|
|
unsigned int memSize;
|
|
unsigned int mode;
|
|
unsigned int gdfIndex;
|
|
diff --git a/u-boot/scripts/config_whitelist.txt b/u-boot/scripts/config_whitelist.txt
|
|
index 826d031..b952c88 100644
|
|
--- a/u-boot/scripts/config_whitelist.txt
|
|
+++ b/u-boot/scripts/config_whitelist.txt
|
|
@@ -6850,6 +6850,7 @@ CONFIG_VIDEO_SM501_8BPP
|
|
CONFIG_VIDEO_SM501_PCI
|
|
CONFIG_VIDEO_STD_TIMINGS
|
|
CONFIG_VIDEO_SUNXI
|
|
+CONFIG_VIDEO_SUNXI2
|
|
CONFIG_VIDEO_VCXK
|
|
CONFIG_VID_FLS_ENV
|
|
CONFIG_VM86
|