mirror of
https://github.com/Fishwaldo/build.git
synced 2025-07-23 21:39:02 +00:00
1494 lines
44 KiB
Diff
1494 lines
44 KiB
Diff
commit b973987576822640f35dbb805a048b1706dc8e6d
|
|
Author: Jernej Skrabec <jernej.skrabec@siol.net>
|
|
Date: Tue Nov 8 01:04:32 2016 +0100
|
|
|
|
sunxi: video: Add video driver for H3 SoC
|
|
|
|
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
|
|
|
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
|
|
index be9fcfd..a414f69 100644
|
|
--- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
|
|
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
|
|
@@ -67,12 +67,20 @@ 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 */
|
|
+#ifdef CONFIG_MACH_SUN8I
|
|
+ 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 */
|
|
+#ifdef CONFIG_MACH_SUN8I
|
|
+ u32 tcon0_clk_cfg; /* 0x118 TCON0 module clock */
|
|
+#else
|
|
u32 lcd0_ch0_clk_cfg; /* 0x118 LCD0 CH0 module clock */
|
|
+#endif
|
|
u32 lcd1_ch0_clk_cfg; /* 0x11c LCD1 CH0 module clock */
|
|
u32 reserved14[3];
|
|
u32 lcd0_ch1_clk_cfg; /* 0x12c LCD0 CH1 module clock */
|
|
@@ -85,7 +93,11 @@ struct sunxi_ccm_reg {
|
|
u32 dmic_clk_cfg; /* 0x148 Digital Mic module clock*/
|
|
u32 reserved15;
|
|
u32 hdmi_clk_cfg; /* 0x150 HDMI module clock */
|
|
+#ifdef CONFIG_MACH_SUN8I
|
|
+ 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 +232,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)
|
|
+
|
|
#define CCM_PLL11_CTRL_N(n) ((((n) - 1) & 0x3f) << 8)
|
|
#define CCM_PLL11_CTRL_SIGMA_DELTA_EN (0x1 << 24)
|
|
#define CCM_PLL11_CTRL_UPD (0x1 << 30)
|
|
@@ -271,9 +292,12 @@ 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_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)
|
|
@@ -346,6 +370,9 @@ struct sunxi_ccm_reg {
|
|
#define CCM_LCD_CH0_CTRL_RST 0
|
|
#define CCM_LCD_CH0_CTRL_GATE (0x1 << 31)
|
|
|
|
+#define CCM_TCON0_CTRL_GATE (0x1 << 31)
|
|
+#define CCM_TCON0_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)
|
|
@@ -355,6 +382,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)
|
|
@@ -363,6 +391,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)
|
|
@@ -390,9 +420,13 @@ 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_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
|
|
@@ -406,6 +440,7 @@ struct sunxi_ccm_reg {
|
|
|
|
/* CCM bits common to all Display Engine (and IEP) clock ctrl regs */
|
|
#define CCM_DE_CTRL_M(n) ((((n) - 1) & 0xf) << 0)
|
|
+#ifndef CONFIG_MACH_SUN8I
|
|
#define CCM_DE_CTRL_PLL_MASK (0xf << 24)
|
|
#define CCM_DE_CTRL_PLL3 (0 << 24)
|
|
#define CCM_DE_CTRL_PLL7 (1 << 24)
|
|
@@ -413,6 +448,11 @@ struct sunxi_ccm_reg {
|
|
#define CCM_DE_CTRL_PLL8 (3 << 24)
|
|
#define CCM_DE_CTRL_PLL9 (4 << 24)
|
|
#define CCM_DE_CTRL_PLL10 (5 << 24)
|
|
+#else
|
|
+#define CCM_DE_CTRL_PLL_MASK (3 << 24)
|
|
+#define CCM_DE_CTRL_PLL6_2X (0 << 24)
|
|
+#define CCM_DE_CTRL_PLL10 (1 << 24)
|
|
+#endif
|
|
#define CCM_DE_CTRL_GATE (1 << 31)
|
|
|
|
/* CCU security switch, H3 only */
|
|
@@ -423,7 +463,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/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
|
|
index 5f93830..9758a14 100644
|
|
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
|
|
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
|
|
@@ -46,7 +46,9 @@
|
|
#define SUNXI_USB1_BASE 0x01c14000
|
|
#endif
|
|
#define SUNXI_SS_BASE 0x01c15000
|
|
+#ifndef CONFIG_MACH_SUN8I_H3
|
|
#define SUNXI_HDMI_BASE 0x01c16000
|
|
+#endif
|
|
#define SUNXI_SPI2_BASE 0x01c17000
|
|
#define SUNXI_SATA_BASE 0x01c18000
|
|
#ifdef CONFIG_SUNXI_GEN_SUN4I
|
|
@@ -163,6 +165,10 @@ defined(CONFIG_MACH_SUN50I)
|
|
#define SUNXI_MP_BASE 0x01e80000
|
|
#define SUNXI_AVG_BASE 0x01ea0000
|
|
|
|
+#ifdef CONFIG_MACH_SUN8I_H3
|
|
+#define SUNXI_HDMI_BASE 0x01ee0000
|
|
+#endif
|
|
+
|
|
#define SUNXI_RTC_BASE 0x01f00000
|
|
#define SUNXI_PRCM_BASE 0x01f01400
|
|
|
|
diff --git a/arch/arm/include/asm/arch-sunxi/display2.h b/arch/arm/include/asm/arch-sunxi/display2.h
|
|
new file mode 100644
|
|
index 0000000..755adeb
|
|
--- /dev/null
|
|
+++ b/arch/arm/include/asm/arch-sunxi/display2.h
|
|
@@ -0,0 +1,261 @@
|
|
+/*
|
|
+ * Sunxi platform display controller register and constant defines
|
|
+ *
|
|
+ * (C) Copyright 2016 Jernej Skrabec <jernej.skrabec@siol.net>
|
|
+ *
|
|
+ * Based on work by:
|
|
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
|
|
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-2.0+
|
|
+ */
|
|
+
|
|
+#ifndef _SUNXI_DISPLAY_H
|
|
+#define _SUNXI_DISPLAY_H
|
|
+
|
|
+struct sun8i_lcdc_reg {
|
|
+ u32 gctl;
|
|
+ u32 gint0;
|
|
+ u32 gint1;
|
|
+ u32 dum0[13];
|
|
+ u32 tcon0_ctl; /* 0x40 */
|
|
+ u32 dum1[19];
|
|
+ u32 tcon1_ctl; /* 0x90 */
|
|
+ u32 basic0; /* XI/YI */
|
|
+ u32 basic1; /* LS_XO/LS_YO */
|
|
+ u32 basic2; /* XO/YO */
|
|
+ u32 basic3; /* HT/HBP */
|
|
+ u32 basic4; /* VT/VBP */
|
|
+ u32 basic5; /* HSPW/VSPW */
|
|
+ u32 dum2;
|
|
+ u32 ps_sync; /* 0xb0 */
|
|
+ u32 dum3[15];
|
|
+ u32 io_pol; /* 0xf0 */
|
|
+ u32 io_tri;
|
|
+ u32 dum4[2];
|
|
+
|
|
+ u32 ceu_ctl; /* 0x100 */
|
|
+ u32 dum5[3];
|
|
+ u32 ceu_rr;
|
|
+ u32 ceu_rg;
|
|
+ u32 ceu_rb;
|
|
+ u32 ceu_rc;
|
|
+ u32 ceu_gr;
|
|
+ u32 ceu_gg;
|
|
+ u32 ceu_gb;
|
|
+ u32 ceu_gc;
|
|
+ u32 ceu_br;
|
|
+ u32 ceu_bg;
|
|
+ u32 ceu_bb;
|
|
+ u32 ceu_bc;
|
|
+ u32 ceu_rv;
|
|
+ u32 ceu_gv;
|
|
+ u32 ceu_bv;
|
|
+ u32 dum6[45];
|
|
+
|
|
+ u32 mux_ctl; /* 0x200 */
|
|
+ u32 dum7[63];
|
|
+
|
|
+ u32 fill_ctl; /* 0x300 */
|
|
+ u32 fill_start0;
|
|
+ u32 fill_end0;
|
|
+ u32 fill_data0;
|
|
+};
|
|
+
|
|
+/* global control */
|
|
+struct de_glb {
|
|
+ u32 ctl;
|
|
+#define DE_MUX_GLB_CTL_rt_en BIT(0)
|
|
+#define DE_MUX_GLB_CTL_finish_irq_en BIT(4)
|
|
+#define DE_MUX_GLB_CTL_rtwb_port BIT(12)
|
|
+ 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;
|
|
+#define VI_CFG_ATTR_en BIT(0)
|
|
+#define VI_CFG_ATTR_fcolor_en BIT(4)
|
|
+#define VI_CFG_ATTR_fmt_SHIFT 8
|
|
+#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
|
|
+#define VI_CFG_ATTR_ui_sel BIT(15)
|
|
+#define VI_CFG_ATTR_top_down BIT(23)
|
|
+ u32 size;
|
|
+ u32 coord;
|
|
+#define VI_N_PLANES 3
|
|
+ u32 pitch[VI_N_PLANES];
|
|
+ u32 top_laddr[VI_N_PLANES];
|
|
+ u32 bot_laddr[VI_N_PLANES];
|
|
+ } cfg[4];
|
|
+ u32 fcolor[4]; /* c0 */
|
|
+ u32 top_haddr[VI_N_PLANES]; /* d0 */
|
|
+ u32 bot_haddr[VI_N_PLANES]; /* dc */
|
|
+ u32 ovl_size[2]; /* e8 */
|
|
+ u32 hori[2]; /* f0 */
|
|
+ u32 vert[2]; /* f8 */
|
|
+};
|
|
+
|
|
+struct de_ui {
|
|
+ struct {
|
|
+ u32 attr;
|
|
+#define UI_CFG_ATTR_en BIT(0)
|
|
+#define UI_CFG_ATTR_alpmod_SHIFT 1
|
|
+#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)
|
|
+#define UI_CFG_ATTR_fcolor_en BIT(4)
|
|
+#define UI_CFG_ATTR_fmt_SHIFT 8
|
|
+#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
|
|
+#define UI_CFG_ATTR_top_down BIT(23)
|
|
+#define UI_CFG_ATTR_alpha_SHIFT 24
|
|
+#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)
|
|
+ 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 */
|
|
+};
|
|
+
|
|
+#define HDMI_EDID_BLOCK_SIZE 128
|
|
+
|
|
+/*
|
|
+ * HDMI register addresses
|
|
+ */
|
|
+#define SUN8I_HDMI_PHY_CTRL_REG (u32*)(SUNXI_HDMI_BASE + 0x10020)
|
|
+#define SUN8I_HDMI_PHY_UNK1_REG (u32*)(SUNXI_HDMI_BASE + 0x10024)
|
|
+#define SUN8I_HDMI_PHY_UNK2_REG (u32*)(SUNXI_HDMI_BASE + 0x10028)
|
|
+#define SUN8I_HDMI_PHY_PLL_REG (u32*)(SUNXI_HDMI_BASE + 0x1002c)
|
|
+#define SUN8I_HDMI_PHY_CLK_REG (u32*)(SUNXI_HDMI_BASE + 0x10030)
|
|
+#define SUN8I_HDMI_PHY_UNK3_REG (u32*)(SUNXI_HDMI_BASE + 0x10034)
|
|
+#define SUN8I_HDMI_PHY_STATUS_REG (u32*)(SUNXI_HDMI_BASE + 0x10038)
|
|
+
|
|
+#define SUN8I_HDMI_IH_I2CM_STAT0 (u32*)(SUNXI_HDMI_BASE + 0x0013)
|
|
+
|
|
+#define SUN8I_HDMI_I2CM_SLAVE (u32*)(SUNXI_HDMI_BASE + 0x0EE0)
|
|
+#define SUN8I_HDMI_I2CM_ADDRESS (u32*)(SUNXI_HDMI_BASE + 0x0EE1)
|
|
+#define SUN8I_HDMI_I2CM_DATAI (u32*)(SUNXI_HDMI_BASE + 0x8EE1)
|
|
+#define SUN8I_HDMI_I2CM_OPERATION (u32*)(SUNXI_HDMI_BASE + 0x0EE2)
|
|
+#define SUN8I_HDMI_I2CM_INT (u32*)(SUNXI_HDMI_BASE + 0x0EE3)
|
|
+#define SUN8I_HDMI_I2CM_DIV (u32*)(SUNXI_HDMI_BASE + 0x8EE3)
|
|
+#define SUN8I_HDMI_I2CM_SEGADDR (u32*)(SUNXI_HDMI_BASE + 0x4EE0)
|
|
+#define SUN8I_HDMI_I2CM_SOFTRSTZ (u32*)(SUNXI_HDMI_BASE + 0x4EE1)
|
|
+#define SUN8I_HDMI_I2CM_SEGPTR (u32*)(SUNXI_HDMI_BASE + 0xCEE0)
|
|
+#define SUN8I_HDMI_I2CM_SS_SCL_HCNT_0_ADDR (u32*)(SUNXI_HDMI_BASE + 0x4EE2)
|
|
+#define SUN8I_HDMI_I2CM_SS_SCL_LCNT_0_ADDR (u32*)(SUNXI_HDMI_BASE + 0xCEE2)
|
|
+
|
|
+/*
|
|
+ * DE register constants.
|
|
+ */
|
|
+/* TODO: move to appropriate place */
|
|
+#define SUN8I_DE_BASE 0x01000000
|
|
+#define SUN8I_DE_GATE_REG (u32*)(SUN8I_DE_BASE + 0x0004)
|
|
+#define SUN8I_DE_MOD_REG (u32*)(SUN8I_DE_BASE + 0x0000)
|
|
+#define SUN8I_DE_RESET_REG (u32*)(SUN8I_DE_BASE + 0x0008)
|
|
+#define SUN8I_DE_DIV_REG (u32*)(SUN8I_DE_BASE + 0x000c)
|
|
+#define SUN8I_DE_SEL_REG (u32*)(SUN8I_DE_BASE + 0x0010)
|
|
+
|
|
+#define DE_MUX0_BASE (u8*)(SUN8I_DE_BASE + 0x00100000)
|
|
+/* MUX registers (addr / MUX base) */
|
|
+#define DE_MUX_GLB_REGS 0x00000 /* global control */
|
|
+#define DE_MUX_BLD_REGS 0x01000 /* alpha blending */
|
|
+#define DE_MUX_CHAN_REGS 0x02000 /* VI/UI overlay channels */
|
|
+#define DE_MUX_CHAN_SZ 0x1000 /* size of a channel */
|
|
+#define DE_MUX_VSU_REGS 0x20000 /* VSU */
|
|
+#define DE_MUX_GSU1_REGS 0x30000 /* GSUs */
|
|
+#define DE_MUX_GSU2_REGS 0x40000
|
|
+#define DE_MUX_GSU3_REGS 0x50000
|
|
+#define DE_MUX_FCE_REGS 0xa0000 /* FCE */
|
|
+#define DE_MUX_BWS_REGS 0xa2000 /* BWS */
|
|
+#define DE_MUX_LTI_REGS 0xa4000 /* LTI */
|
|
+#define DE_MUX_PEAK_REGS 0xa6000 /* PEAK */
|
|
+#define DE_MUX_ASE_REGS 0xa8000 /* ASE */
|
|
+#define DE_MUX_FCC_REGS 0xaa000 /* FCC */
|
|
+#define DE_MUX_DCSC_REGS 0xb0000 /* DCSC/SMBL */
|
|
+
|
|
+#define DE2_FORMAT_ARGB_8888 0
|
|
+#define DE2_FORMAT_BGRA_8888 3
|
|
+#define DE2_FORMAT_XRGB_8888 4
|
|
+#define DE2_FORMAT_RGB_888 8
|
|
+#define DE2_FORMAT_BGR_888 9
|
|
+
|
|
+/* coordinates and sizes */
|
|
+#define XY(x, y) (((y) << 16) | (x))
|
|
+#define WH(w, h) (((h - 1) << 16) | (w - 1))
|
|
+
|
|
+/*
|
|
+ * LCDC register constants.
|
|
+ */
|
|
+#define SUN8I_TCON_GCTL_TCON_En (1 << 31)
|
|
+#define SUN8I_TCON_GINT0_TCON1_Vb_Int_En (1 << 30)
|
|
+#define SUN8I_TCON_GINT0_TCON1_Vb_Int_Flag (1 << 14)
|
|
+#define SUN8I_TCON0_CTL_TCON_En (1 << 31)
|
|
+#define SUN8I_TCON1_CTL_TCON_En (1 << 31)
|
|
+#define SUN8I_TCON1_CTL_Interlace_En (1 << 20)
|
|
+#define SUN8I_TCON1_CTL_Start_Delay_SHIFT 4
|
|
+/*#define SUN8I_TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)*/
|
|
+#define SUN8I_TCON1_IO_POL_IO0_inv (1 << 24)
|
|
+#define SUN8I_TCON1_IO_POL_IO1_inv (1 << 25)
|
|
+#define SUN8I_TCON1_IO_POL_IO2_inv (1 << 26)
|
|
+#define SUN8I_TCON_CEU_CTL_ceu_en (1 << 31)
|
|
+
|
|
+#define SUNXI_LCDC_X(x) (((x) - 1) << 16)
|
|
+#define SUNXI_LCDC_Y(y) (((y) - 1) << 0)
|
|
+#define SUNXI_LCDC_TCON_VSYNC_MASK (1 << 24)
|
|
+#define SUNXI_LCDC_TCON_HSYNC_MASK (1 << 25)
|
|
+#define SUNXI_LCDC_CTRL_IO_MAP_MASK (1 << 0)
|
|
+#define SUNXI_LCDC_CTRL_IO_MAP_TCON0 (0 << 0)
|
|
+#define SUNXI_LCDC_CTRL_IO_MAP_TCON1 (1 << 0)
|
|
+#define SUNXI_LCDC_CTRL_TCON_ENABLE (1 << 31)
|
|
+#define SUNXI_LCDC_TCON1_CTRL_SRC_BLUE (1 << 1)
|
|
+#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 SUNXI_HDMI_HPD_DETECT (1 << 19)
|
|
+
|
|
+#define SUN8I_HMDI_DDC_CTRL_RESET (1 << 0)
|
|
+#define SUN8I_HMDI_DDC_ADDR_SLAVE_ADDR (0x50 << 0)
|
|
+#define SUN8I_HMDI_DDC_ADDR_SEG_ADDR (0x30 << 0)
|
|
+
|
|
+
|
|
+#endif /* _SUNXI_DISPLAY_H */
|
|
diff --git a/arch/arm/mach-sunxi/clock_sun6i.c b/arch/arm/mach-sunxi/clock_sun6i.c
|
|
index ed8cd9b..bcf9aa1 100644
|
|
--- a/arch/arm/mach-sunxi/clock_sun6i.c
|
|
+++ b/arch/arm/mach-sunxi/clock_sun6i.c
|
|
@@ -141,6 +141,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 =
|
|
@@ -213,6 +224,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);
|
|
+}
|
|
+
|
|
#ifdef CONFIG_MACH_SUN8I_A33
|
|
void clock_set_pll11(unsigned int clk, bool sigma_delta_enable)
|
|
{
|
|
@@ -267,6 +295,7 @@ unsigned int clock_get_mipi_pll(void)
|
|
return ((src / 1000) * n * k / m) * 1000;
|
|
}
|
|
|
|
+#ifndef CONFIG_MACH_SUN8I
|
|
void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz)
|
|
{
|
|
int pll = clock_get_pll6() * 2;
|
|
@@ -278,3 +307,4 @@ void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz)
|
|
writel(CCM_DE_CTRL_GATE | CCM_DE_CTRL_PLL6_2X | CCM_DE_CTRL_M(div),
|
|
clk_cfg);
|
|
}
|
|
+#endif
|
|
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
|
|
index c0ffeb3..77fdc8f 100644
|
|
--- a/board/sunxi/Kconfig
|
|
+++ b/board/sunxi/Kconfig
|
|
@@ -458,7 +458,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 && !MACH_SUN50I
|
|
default y
|
|
---help---
|
|
Say Y here to add support for using a cfb console on the HDMI, LCD
|
|
@@ -467,7 +467,7 @@ config VIDEO
|
|
|
|
config VIDEO_HDMI
|
|
bool "HDMI output support"
|
|
- depends on VIDEO && !MACH_SUN8I
|
|
+ depends on VIDEO
|
|
default y
|
|
---help---
|
|
Say Y here to add support for outputting video over HDMI.
|
|
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
|
|
index db34904..d05b745 100644
|
|
--- a/drivers/video/Makefile
|
|
+++ b/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_SUNXI_H3) += sun8i_display.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/drivers/video/sun8i_display.c b/drivers/video/sun8i_display.c
|
|
new file mode 100644
|
|
index 0000000..e596e4c
|
|
--- /dev/null
|
|
+++ b/drivers/video/sun8i_display.c
|
|
@@ -0,0 +1,928 @@
|
|
+/*
|
|
+ * 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 <video_fb.h>
|
|
+#include "videomodes.h"
|
|
+
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
+
|
|
+enum sunxi_monitor {
|
|
+ sunxi_monitor_none,
|
|
+ sunxi_monitor_dvi,
|
|
+ sunxi_monitor_hdmi,
|
|
+};
|
|
+#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;
|
|
+
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+
|
|
+static void sun8i_hdmi_phy_init(void)
|
|
+{
|
|
+ unsigned long tmo;
|
|
+ u32 tmp;
|
|
+
|
|
+ writel(0, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(0));
|
|
+ udelay(5);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(16));
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(1));
|
|
+ udelay(10);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(2));
|
|
+ udelay(5);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(3));
|
|
+ udelay(40);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(19));
|
|
+ udelay(100);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(18));
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, 7 << 4);
|
|
+
|
|
+ /* Note that Allwinner code doesn't fail in case of timeout */
|
|
+ tmo = timer_get_us() + 2000;
|
|
+ while ((readl(SUN8I_HDMI_PHY_STATUS_REG) & 0x80) == 0) {
|
|
+ if (timer_get_us() > tmo) {
|
|
+ printf("Warning: HDMI phy init timeout!\n");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, 0xf << 8);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, BIT(7));
|
|
+
|
|
+ writel(0x39dc5040, SUN8I_HDMI_PHY_PLL_REG);
|
|
+ writel(0x80084343, SUN8I_HDMI_PHY_CLK_REG);
|
|
+ udelay(10000);
|
|
+ writel(1, SUN8I_HDMI_PHY_UNK3_REG);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(25));
|
|
+ udelay(100000);
|
|
+ tmp = (readl(SUN8I_HDMI_PHY_STATUS_REG) & 0x1f800) >> 11;
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(31) | BIT(30));
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, tmp);
|
|
+ writel(0x01FF0F7F, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ writel(0x80639000, SUN8I_HDMI_PHY_UNK1_REG);
|
|
+ writel(0x0F81C405, SUN8I_HDMI_PHY_UNK2_REG);
|
|
+
|
|
+ /* enable read access to HDMI controller*/
|
|
+ writel(0x54524545, SUNXI_HDMI_BASE + 0x10010);
|
|
+
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x8080);
|
|
+
|
|
+ udelay(1);
|
|
+
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0xF01F);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8403);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x904C);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x904E);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0xD04C);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8250);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8A50);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8272);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x40C0);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x86F0);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x0EE3);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8EE2);
|
|
+ writeb(0xf0, SUNXI_HDMI_BASE + 0xA049);
|
|
+ writeb(0x1e, SUNXI_HDMI_BASE + 0xB045);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x00C1);
|
|
+ writeb(0x03, SUNXI_HDMI_BASE + 0x00C1);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x00C0);
|
|
+ writeb(0x10, SUNXI_HDMI_BASE + 0x40C1);
|
|
+ writeb(0xfd, SUNXI_HDMI_BASE + 0x0081);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x0081);
|
|
+ writeb(0xfd, SUNXI_HDMI_BASE + 0x0081);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x0010);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x0011);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8010);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8011);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x0013);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8012);
|
|
+ writeb(0xff, SUNXI_HDMI_BASE + 0x8013);
|
|
+}
|
|
+
|
|
+static int sun8i_hdmi_hpd_detect(int hpd_delay)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_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);
|
|
+
|
|
+ sun8i_hdmi_phy_init();
|
|
+
|
|
+ while (timer_get_us() < tmo) {
|
|
+ if (readl(SUN8I_HDMI_PHY_STATUS_REG) & 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;
|
|
+
|
|
+ writel(0, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ 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 sun8i_hdmi_ddc_wait_i2c_done(int msec)
|
|
+{
|
|
+ u32 val;
|
|
+ ulong start;
|
|
+
|
|
+ start = get_timer(0);
|
|
+ do {
|
|
+ val = readb(SUN8I_HDMI_IH_I2CM_STAT0);
|
|
+ writeb(val, SUN8I_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)
|
|
+{
|
|
+ int shift = (block % 2) * 0x80;
|
|
+ int trytime = 5;
|
|
+ int edid_read_err = 0;
|
|
+ u32 op = (block == 0) ? 1 : 2;
|
|
+ int n;
|
|
+
|
|
+ writeb(block >> 1, SUN8I_HDMI_I2CM_SEGPTR);
|
|
+
|
|
+ while (trytime--) {
|
|
+ edid_read_err = 0;
|
|
+
|
|
+ for (n = 0; n < HDMI_EDID_BLOCK_SIZE; n++) {
|
|
+ writeb(shift + n, SUN8I_HDMI_I2CM_ADDRESS);
|
|
+ writeb(op, SUN8I_HDMI_I2CM_OPERATION);
|
|
+
|
|
+ if (sun8i_hdmi_ddc_wait_i2c_done(10)) {
|
|
+ edid_read_err = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ *buf++ = readb(SUN8I_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 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, SUN8I_HDMI_I2CM_SOFTRSTZ);
|
|
+
|
|
+ writeb(0x05, SUN8I_HDMI_I2CM_DIV);
|
|
+ writeb(0x08, SUN8I_HDMI_I2CM_INT);
|
|
+ writeb(0xd8, SUN8I_HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
|
|
+ writeb(0xfe, SUN8I_HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
|
|
+ writeb(SUN8I_HMDI_DDC_ADDR_SLAVE_ADDR, SUN8I_HDMI_I2CM_SLAVE);
|
|
+ writeb(SUN8I_HMDI_DDC_ADDR_SEG_ADDR, SUN8I_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;
|
|
+
|
|
+ clock_set_pll10(432000000);
|
|
+
|
|
+ /* Set DE parent to pll10 */
|
|
+ clrsetbits_le32(&ccm->de_clk_cfg, CCM_DE_CTRL_PLL_MASK,
|
|
+ CCM_DE_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_DE_CTRL_GATE);
|
|
+}
|
|
+
|
|
+static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
|
|
+ unsigned int address)
|
|
+{
|
|
+ struct de_glb * const de_glb_regs =
|
|
+ (struct de_glb *)(DE_MUX0_BASE + DE_MUX_GLB_REGS);
|
|
+ struct de_bld * const de_bld_regs =
|
|
+ (struct de_bld *)(DE_MUX0_BASE + DE_MUX_BLD_REGS);
|
|
+ struct de_ui * const de_ui_regs =
|
|
+ (struct de_ui *)(DE_MUX0_BASE + DE_MUX_CHAN_REGS +
|
|
+ DE_MUX_CHAN_SZ * 1);
|
|
+ u32 size = WH(mode->xres, mode->yres);
|
|
+ int channel, i;
|
|
+ u32 data;
|
|
+
|
|
+ /* enable clock */
|
|
+ setbits_le32(SUN8I_DE_RESET_REG, 1);
|
|
+ setbits_le32(SUN8I_DE_GATE_REG, 1);
|
|
+ setbits_le32(SUN8I_DE_MOD_REG, 1);
|
|
+
|
|
+ clrbits_le32(SUN8I_DE_SEL_REG, 1);
|
|
+
|
|
+ writel(DE_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_MUX0_BASE + DE_MUX_CHAN_REGS +
|
|
+ DE_MUX_CHAN_SZ * channel;
|
|
+ memset(chan, 0, channel == 0 ?
|
|
+ sizeof(struct de_vi) : sizeof(struct de_ui));
|
|
+ }
|
|
+
|
|
+ memset(de_bld_regs, 0, 0x44);
|
|
+ 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(0x03010301, &de_bld_regs->bld_mode[1]);
|
|
+
|
|
+ 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);
|
|
+
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ writel(0xff000000, &de_bld_regs->attr[i].fcolor);
|
|
+ writel(size, &de_bld_regs->attr[i].insize);
|
|
+ }
|
|
+
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_VSU_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_GSU1_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_GSU2_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_GSU3_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_FCE_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_BWS_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_LTI_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_PEAK_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_ASE_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_FCC_REGS);
|
|
+ writel(0, DE_MUX0_BASE + DE_MUX_DCSC_REGS);
|
|
+
|
|
+ data = UI_CFG_ATTR_en | (DE2_FORMAT_XRGB_8888 << UI_CFG_ATTR_fmt_SHIFT) |
|
|
+ (1 << UI_CFG_ATTR_alpmod_SHIFT) | (0xff << UI_CFG_ATTR_alpha_SHIFT);
|
|
+ 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(void)
|
|
+{
|
|
+ struct de_glb * const de_glb_regs =
|
|
+ (struct de_glb *)(DE_MUX0_BASE + DE_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;
|
|
+
|
|
+ if (dotclock <= 27000)
|
|
+ x = 11;
|
|
+ else if (dotclock <= 74250)
|
|
+ x = 4;
|
|
+ else if (dotclock <= 148500)
|
|
+ x = 2;
|
|
+ else if (dotclock <= 297000)
|
|
+ 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);
|
|
+ printf("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
|
|
+ dotclock, (clock_get_pll3() / 1000) / x,
|
|
+ best_n, best_m, x);
|
|
+
|
|
+ writel(CCM_TCON0_CTRL_GATE | CCM_TCON0_CTRL_M(x),
|
|
+ &ccm->tcon0_clk_cfg);
|
|
+
|
|
+ *clk_div = x;
|
|
+}
|
|
+
|
|
+static void sunxi_lcdc_init(void)
|
|
+{
|
|
+ struct sunxi_ccm_reg * const ccm =
|
|
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
+ struct sun8i_lcdc_reg * const lcdc =
|
|
+ (struct sun8i_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);
|
|
+
|
|
+ /* Init lcdc */
|
|
+ clrbits_le32(&lcdc->tcon0_ctl, SUN8I_TCON0_CTL_TCON_En); /* Disable tcon0 */
|
|
+ clrbits_le32(&lcdc->gctl, SUN8I_TCON_GCTL_TCON_En); /* Disable tcon globally */
|
|
+ writel(0, &lcdc->gint0); /* Disable all interrupts */
|
|
+
|
|
+ /* Set all io lines to tristate */
|
|
+ writel(0x0fffffff, &lcdc->io_tri);
|
|
+}
|
|
+
|
|
+static void sunxi_lcdc_enable(void)
|
|
+{
|
|
+ struct sun8i_lcdc_reg * const lcdc =
|
|
+ (struct sun8i_lcdc_reg *)SUNXI_LCD0_BASE;
|
|
+
|
|
+ setbits_le32(&lcdc->gctl, SUN8I_TCON_GCTL_TCON_En);
|
|
+}
|
|
+
|
|
+static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode, int tcon)
|
|
+{
|
|
+ int delay;
|
|
+
|
|
+ delay = mode->lower_margin + mode->vsync_len + mode->upper_margin;
|
|
+ if (mode->vmode == FB_VMODE_INTERLACED)
|
|
+ delay /= 2;
|
|
+ if (tcon == 1)
|
|
+ delay -= 2;
|
|
+
|
|
+ return (delay > 31) ? 31 : delay;
|
|
+}
|
|
+
|
|
+#if defined CONFIG_VIDEO_HDMI
|
|
+static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
|
|
+ int *clk_div)
|
|
+{
|
|
+ struct sun8i_lcdc_reg * const lcdc =
|
|
+ (struct sun8i_lcdc_reg *)SUNXI_LCD0_BASE;
|
|
+ int bp, clk_delay, total, yres;
|
|
+
|
|
+ setbits_le32(&lcdc->gctl, SUN8I_TCON_GCTL_TCON_En);
|
|
+
|
|
+ clk_delay = sunxi_lcdc_get_clk_delay(mode, 1);
|
|
+ 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_ctl);
|
|
+
|
|
+ yres = mode->yres;
|
|
+ if (mode->vmode == FB_VMODE_INTERLACED)
|
|
+ yres /= 2;
|
|
+ writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
|
|
+ &lcdc->basic0);
|
|
+ writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
|
|
+ &lcdc->basic1);
|
|
+ writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
|
|
+ &lcdc->basic2);
|
|
+
|
|
+ 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->basic3);
|
|
+
|
|
+ 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->basic4);
|
|
+
|
|
+ writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
|
|
+ &lcdc->basic5);
|
|
+
|
|
+ writel(0, &lcdc->ceu_ctl);
|
|
+ writel(0, &lcdc->fill_ctl);
|
|
+
|
|
+ sunxi_lcdc_pll_set(mode->pixclock_khz, clk_div);
|
|
+}
|
|
+#endif /* CONFIG_VIDEO_HDMI */
|
|
+
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+
|
|
+static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
|
|
+{
|
|
+ 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(SUNXI_HDMI_BASE + 0x0040, 0x08);
|
|
+ writeb(0x60, SUNXI_HDMI_BASE + 0x4045);
|
|
+ writeb(tmp, SUNXI_HDMI_BASE + 0xC044);
|
|
+ writeb(0x88, SUNXI_HDMI_BASE + 0xC045);
|
|
+}
|
|
+
|
|
+static int hdmi_phy_set(u32 divider)
|
|
+{
|
|
+ u32 tmp;
|
|
+
|
|
+ switch(divider)
|
|
+ {
|
|
+ case 1:
|
|
+ writel(0x30dc5fc0, SUN8I_HDMI_PHY_PLL_REG);
|
|
+ writel(0x800863C0, SUN8I_HDMI_PHY_CLK_REG);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, SUN8I_HDMI_PHY_UNK3_REG);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(25));
|
|
+ mdelay(200);
|
|
+ tmp = (readl(SUN8I_HDMI_PHY_STATUS_REG) & 0x1f800) >> 11;
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(31) | BIT(30));
|
|
+ if (tmp < 0x3d)
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, tmp + 2);
|
|
+ else
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, 0x3f);
|
|
+ mdelay(100);
|
|
+ writel(0x01FFFF7F, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ writel(0x8063b000, SUN8I_HDMI_PHY_UNK1_REG);
|
|
+ writel(0x0F8246B5, SUN8I_HDMI_PHY_UNK2_REG);
|
|
+ break;
|
|
+ case 2:
|
|
+ writel(0x39dc5040, SUN8I_HDMI_PHY_PLL_REG);
|
|
+ writel(0x80084381, SUN8I_HDMI_PHY_CLK_REG);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, SUN8I_HDMI_PHY_UNK3_REG);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(25));
|
|
+ mdelay(100);
|
|
+ tmp = (readl(SUN8I_HDMI_PHY_STATUS_REG) & 0x1f800) >> 11;
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(31) | BIT(30));
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, tmp);
|
|
+ writel(0x01FFFF7F, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ writel(0x8063a800, SUN8I_HDMI_PHY_UNK1_REG);
|
|
+ writel(0x0F81C485, SUN8I_HDMI_PHY_UNK2_REG);
|
|
+ break;
|
|
+ case 4:
|
|
+ writel(0x39dc5040, SUN8I_HDMI_PHY_PLL_REG);
|
|
+ writel(0x80084343, SUN8I_HDMI_PHY_CLK_REG);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, SUN8I_HDMI_PHY_UNK3_REG);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(25));
|
|
+ mdelay(100);
|
|
+ tmp = (readl(SUN8I_HDMI_PHY_STATUS_REG) & 0x1f800) >> 11;
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(31) | BIT(30));
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, tmp);
|
|
+ writel(0x01FFFF7F, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ writel(0x8063b000, SUN8I_HDMI_PHY_UNK1_REG);
|
|
+ writel(0x0F81C405, SUN8I_HDMI_PHY_UNK2_REG);
|
|
+ break;
|
|
+ case 11:
|
|
+ writel(0x39dc5040, SUN8I_HDMI_PHY_PLL_REG);
|
|
+ writel(0x8008430a, SUN8I_HDMI_PHY_CLK_REG);
|
|
+ mdelay(10);
|
|
+ writel(0x00000001, SUN8I_HDMI_PHY_UNK3_REG);
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(25));
|
|
+ mdelay(100);
|
|
+ tmp = (readl(SUN8I_HDMI_PHY_STATUS_REG) & 0x1f800) >> 11;
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, BIT(31) | BIT(30));
|
|
+ setbits_le32(SUN8I_HDMI_PHY_PLL_REG, tmp);
|
|
+ writel(0x01FFFF7F, SUN8I_HDMI_PHY_CTRL_REG);
|
|
+ writel(0x8063b000, SUN8I_HDMI_PHY_UNK1_REG);
|
|
+ writel(0x0F81C405, SUN8I_HDMI_PHY_UNK2_REG);
|
|
+ break;
|
|
+ default:
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
|
|
+ int clk_div)
|
|
+{
|
|
+ u8 invidconf, v_blanking;
|
|
+ u32 h_blanking;
|
|
+
|
|
+ if(hdmi_phy_set(clk_div) != 0) {
|
|
+ printf("HDMI divider is invalid!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ 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 | 0x10, SUNXI_HDMI_BASE + 0x0040);
|
|
+ writeb(((invidconf < 96) ? 0x03 : 0x00), SUNXI_HDMI_BASE + 0x10001);
|
|
+
|
|
+ writeb(mode->xres >> 8, SUNXI_HDMI_BASE + 0x8040);
|
|
+ writeb(mode->xres, SUNXI_HDMI_BASE + 0x0041);
|
|
+ writeb(mode->yres >> 8, SUNXI_HDMI_BASE + 0x8042);
|
|
+ writeb(mode->yres, SUNXI_HDMI_BASE + 0x0043);
|
|
+ writeb(mode->vsync_len, SUNXI_HDMI_BASE + 0x4043);
|
|
+ writeb(h_blanking >> 8, SUNXI_HDMI_BASE + 0x0042);
|
|
+ writeb(h_blanking, SUNXI_HDMI_BASE + 0x8041);
|
|
+ writeb(mode->lower_margin, SUNXI_HDMI_BASE + 0x4042);
|
|
+ writeb(mode->right_margin >> 8, SUNXI_HDMI_BASE + 0x4041);
|
|
+ writeb(mode->right_margin, SUNXI_HDMI_BASE + 0x4040);
|
|
+ writeb(mode->hsync_len >> 8, SUNXI_HDMI_BASE + 0xC041);
|
|
+ writeb(mode->hsync_len, SUNXI_HDMI_BASE + 0xC040);
|
|
+ writeb(v_blanking, SUNXI_HDMI_BASE + 0x8043);
|
|
+
|
|
+ writeb(0x0c, SUNXI_HDMI_BASE + 0x0045);
|
|
+ writeb(0x20, SUNXI_HDMI_BASE + 0x8044);
|
|
+ writeb(0x01, SUNXI_HDMI_BASE + 0x8045);
|
|
+ writeb(0x0b, SUNXI_HDMI_BASE + 0x0046);
|
|
+ writeb(0x16, SUNXI_HDMI_BASE + 0x0047);
|
|
+ writeb(0x21, SUNXI_HDMI_BASE + 0x8046);
|
|
+
|
|
+ writeb(0x40, SUNXI_HDMI_BASE + 0x0401);
|
|
+ writeb(0x07, SUNXI_HDMI_BASE + 0x8400);
|
|
+
|
|
+ // default value, written 0 by rk_hdmi
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x8401);
|
|
+
|
|
+ writeb(0x47, SUNXI_HDMI_BASE + 0x0402);
|
|
+ writeb(0x01, SUNXI_HDMI_BASE + 0x0800);
|
|
+ writeb(0x07, SUNXI_HDMI_BASE + 0x0801);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x8800);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x8801);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x0802);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x0803);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x8802);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x8803);
|
|
+
|
|
+ if (sunxi_display.monitor == sunxi_monitor_hdmi)
|
|
+ sunxi_hdmi_setup_info_frames(mode);
|
|
+
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x0082);
|
|
+ writeb(0x00, SUNXI_HDMI_BASE + 0x0081);
|
|
+}
|
|
+
|
|
+static void sunxi_hdmi_enable(void)
|
|
+{
|
|
+ setbits_le32(SUN8I_HDMI_PHY_CTRL_REG, 0xf << 12);
|
|
+ printf("hdmi enabled\n");
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_VIDEO_HDMI */
|
|
+
|
|
+static void sunxi_engines_init(void)
|
|
+{
|
|
+ sunxi_composer_init();
|
|
+ sunxi_lcdc_init();
|
|
+}
|
|
+
|
|
+static void sunxi_mode_set(const struct ctfb_res_modes *mode,
|
|
+ unsigned int address)
|
|
+{
|
|
+ int __maybe_unused clk_div;
|
|
+
|
|
+ switch (sunxi_display.monitor) {
|
|
+ case sunxi_monitor_none:
|
|
+ break;
|
|
+ case sunxi_monitor_dvi:
|
|
+ case sunxi_monitor_hdmi:
|
|
+#ifdef CONFIG_VIDEO_HDMI
|
|
+ sunxi_composer_mode_set(mode, address);
|
|
+ sunxi_lcdc_tcon0_mode_set(mode, &clk_div);
|
|
+ sunxi_hdmi_mode_set(mode, clk_div);
|
|
+ sunxi_composer_enable();
|
|
+ sunxi_lcdc_enable();
|
|
+ sunxi_hdmi_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";
|
|
+ }
|
|
+ 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 enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
|
|
+{
|
|
+ if (allow_hdmi && sunxi_has_hdmi())
|
|
+ return sunxi_monitor_dvi;
|
|
+ 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 = sun8i_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;
|
|
+ }
|
|
+
|
|
+ if (overscan_x == -1)
|
|
+ overscan_x = 0;
|
|
+ if (overscan_y == -1)
|
|
+ overscan_y = 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;
|
|
+}
|
|
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
|
|
index e0464df..c63c1bc 100644
|
|
--- a/include/configs/sunxi-common.h
|
|
+++ b/include/configs/sunxi-common.h
|
|
@@ -284,9 +284,13 @@ extern int soft_i2c_gpio_scl;
|
|
#define CONFIG_SUNXI_MAX_FB_SIZE (16 << 20)
|
|
|
|
/* Do we want to initialize a simple FB? */
|
|
+#ifndef CONFIG_MACH_SUN8I_H3
|
|
#define CONFIG_VIDEO_DT_SIMPLEFB
|
|
|
|
#define CONFIG_VIDEO_SUNXI
|
|
+#else
|
|
+#define CONFIG_VIDEO_SUNXI_H3
|
|
+#endif
|
|
|
|
#define CONFIG_VIDEO_LOGO
|
|
#define CONFIG_VIDEO_STD_TIMINGS
|
|
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
|
|
index 11b5a22..cef476f 100644
|
|
--- a/scripts/config_whitelist.txt
|
|
+++ b/scripts/config_whitelist.txt
|
|
@@ -8270,6 +8270,7 @@ CONFIG_VIDEO_SM501_8BPP
|
|
CONFIG_VIDEO_SM501_PCI
|
|
CONFIG_VIDEO_STD_TIMINGS
|
|
CONFIG_VIDEO_SUNXI
|
|
+CONFIG_VIDEO_SUNXI_H3
|
|
CONFIG_VIDEO_VCXK
|
|
CONFIG_VID_FLS_ENV
|
|
CONFIG_VM86
|