[PATCH v2 18/19] video: sunxi: Add DW HDMI PHY driver
Jernej Skrabec
jernej.skrabec at siol.net
Sat Mar 6 20:54:36 CET 2021
This commit adds standalone driver for DW HDMI PHY. It deprecates code
which is included in sunxi dw-hdmi platform driver.
Signed-off-by: Jernej Skrabec <jernej.skrabec at siol.net>
---
arch/arm/mach-sunxi/Kconfig | 1 +
drivers/video/sunxi/Makefile | 2 +-
drivers/video/sunxi/sunxi_dw_hdmi_phy.c | 423 ++++++++++++++++++++++++
drivers/video/sunxi/sunxi_dw_hdmi_phy.h | 24 ++
4 files changed, 449 insertions(+), 1 deletion(-)
create mode 100644 drivers/video/sunxi/sunxi_dw_hdmi_phy.c
create mode 100644 drivers/video/sunxi/sunxi_dw_hdmi_phy.h
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 34ef1f4b030f..5f2df7727357 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -973,6 +973,7 @@ config VIDEO_DE2
select CLK_SUN8I_DE2
select DM_VIDEO
select DISPLAY
+ select PHY
select VIDEO_DW_HDMI
imply VIDEO_DT_SIMPLEFB
default y
diff --git a/drivers/video/sunxi/Makefile b/drivers/video/sunxi/Makefile
index 4321673312bf..22ec17fb4fd2 100644
--- a/drivers/video/sunxi/Makefile
+++ b/drivers/video/sunxi/Makefile
@@ -4,4 +4,4 @@
# Wolfgang Denk, DENX Software Engineering, wd at denx.de.
obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o simplefb_common.o lcdc.o tve_common.o ../videomodes.o
-obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o simplefb_common.o lcdc.o sunxi_lcd.o
+obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o sunxi_dw_hdmi_phy.o simplefb_common.o lcdc.o sunxi_lcd.o
diff --git a/drivers/video/sunxi/sunxi_dw_hdmi_phy.c b/drivers/video/sunxi/sunxi_dw_hdmi_phy.c
new file mode 100644
index 000000000000..bed5c2fdfe81
--- /dev/null
+++ b/drivers/video/sunxi/sunxi_dw_hdmi_phy.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Allwinner DW HDMI PHY driver
+ *
+ * (C) Copyright 2021 Jernej Skrabec <jernej.skrabec at siol.net>
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <reset.h>
+#include <time.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include "sunxi_dw_hdmi_phy.h"
+
+#define SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK BIT(0)
+#define SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK GENMASK(15, 8)
+#define SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC BIT(8)
+#define SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC BIT(9)
+#define SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK GENMASK(23, 16)
+#define SUN8I_HDMI_PHY_DBG_CTRL_ADDR(addr) (addr << 16)
+
+#define SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN BIT(31)
+
+#define SUN8I_HDMI_PHY_READ_EN_MAGIC 0x54524545
+
+#define SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC 0x42494E47
+
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_SWI BIT(31)
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_PWEND BIT(30)
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_PWENC BIT(29)
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW BIT(28)
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_SVRCAL(x) ((x) << 26)
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(x) ((x) << 24)
+#define SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT BIT(23)
+#define SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT BIT(22)
+#define SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT BIT(21)
+#define SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT BIT(20)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL BIT(19)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG BIT(18)
+#define SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS BIT(17)
+#define SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN BIT(16)
+#define SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK GENMASK(15, 12)
+#define SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL (0xf << 12)
+#define SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK BIT(11)
+#define SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 BIT(10)
+#define SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 BIT(9)
+#define SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 BIT(8)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK BIT(7)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 BIT(6)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 BIT(5)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 BIT(4)
+#define SUN8I_HDMI_PHY_ANA_CFG1_CKEN BIT(3)
+#define SUN8I_HDMI_PHY_ANA_CFG1_LDOEN BIT(2)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENVBS BIT(1)
+#define SUN8I_HDMI_PHY_ANA_CFG1_ENBI BIT(0)
+
+#define SUN8I_HDMI_PHY_ANA_CFG2_M_EN BIT(31)
+#define SUN8I_HDMI_PHY_ANA_CFG2_PLLDBEN BIT(30)
+#define SUN8I_HDMI_PHY_ANA_CFG2_SEN BIT(29)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_HPDPD BIT(28)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_HPDEN BIT(27)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_PLRCK BIT(26)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_PLR(x) ((x) << 23)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK BIT(22)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN BIT(21)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_CD(x) ((x) << 19)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(x) ((x) << 17)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK BIT(16)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW BIT(15)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(x) ((x) << 13)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(x) ((x) << 10)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_BOOSTCK(x) ((x) << 8)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_BOOST(x) ((x) << 6)
+#define SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(x) ((x) << 0)
+
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_SLOWCK(x) ((x) << 30)
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_SLOW(x) ((x) << 28)
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(x) ((x) << 18)
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(x) ((x) << 14)
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_EMPCK(x) ((x) << 11)
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(x) ((x) << 7)
+#define SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(x) ((x) << 4)
+#define SUN8I_HDMI_PHY_ANA_CFG3_SDAPD BIT(3)
+#define SUN8I_HDMI_PHY_ANA_CFG3_SDAEN BIT(2)
+#define SUN8I_HDMI_PHY_ANA_CFG3_SCLPD BIT(1)
+#define SUN8I_HDMI_PHY_ANA_CFG3_SCLEN BIT(0)
+
+#define SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 BIT(31)
+#define SUN8I_HDMI_PHY_PLL_CFG1_REG_OD BIT(30)
+#define SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN BIT(29)
+#define SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN BIT(28)
+#define SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 BIT(27)
+#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK BIT(26)
+#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_SHIFT 26
+#define SUN8I_HDMI_PHY_PLL_CFG1_PLLEN BIT(25)
+#define SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(x) ((x) << 22)
+#define SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(x) ((x) << 20)
+#define SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN BIT(19)
+#define SUN8I_HDMI_PHY_PLL_CFG1_CS BIT(18)
+#define SUN8I_HDMI_PHY_PLL_CFG1_CP_S(x) ((x) << 13)
+#define SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(x) ((x) << 7)
+#define SUN8I_HDMI_PHY_PLL_CFG1_BWS BIT(6)
+#define SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK GENMASK(5, 0)
+#define SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT 0
+
+#define SUN8I_HDMI_PHY_PLL_CFG2_SV_H BIT(31)
+#define SUN8I_HDMI_PHY_PLL_CFG2_PDCLKSEL(x) ((x) << 29)
+#define SUN8I_HDMI_PHY_PLL_CFG2_CLKSTEP(x) ((x) << 27)
+#define SUN8I_HDMI_PHY_PLL_CFG2_PSET(x) ((x) << 24)
+#define SUN8I_HDMI_PHY_PLL_CFG2_PCLK_SEL BIT(23)
+#define SUN8I_HDMI_PHY_PLL_CFG2_AUTOSYNC_DIS BIT(22)
+#define SUN8I_HDMI_PHY_PLL_CFG2_VREG2_OUT_EN BIT(21)
+#define SUN8I_HDMI_PHY_PLL_CFG2_VREG1_OUT_EN BIT(20)
+#define SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN BIT(19)
+#define SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN(x) ((x) << 16)
+#define SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(x) ((x) << 12)
+#define SUN8I_HDMI_PHY_PLL_CFG2_VCO_RST_IN BIT(11)
+#define SUN8I_HDMI_PHY_PLL_CFG2_SINT_FRAC BIT(10)
+#define SUN8I_HDMI_PHY_PLL_CFG2_SDIV2 BIT(9)
+#define SUN8I_HDMI_PHY_PLL_CFG2_S(x) ((x) << 6)
+#define SUN8I_HDMI_PHY_PLL_CFG2_S6P25_7P5 BIT(5)
+#define SUN8I_HDMI_PHY_PLL_CFG2_S5_7 BIT(4)
+#define SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK GENMASK(3, 0)
+#define SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_SHIFT 0
+#define SUN8I_HDMI_PHY_PLL_CFG2_PREDIV(x) (((x) - 1) << 0)
+
+#define SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2 BIT(0)
+
+#define SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT 11
+#define SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK GENMASK(16, 11)
+#define SUN8I_HDMI_PHY_ANA_STS_RCALEND2D BIT(7)
+#define SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK GENMASK(5, 0)
+
+struct sunxi_hdmi_phy {
+ u32 dbg_ctrl;
+ u32 rext_ctrl;
+ u32 res1[2];
+ u32 read_en;
+ u32 unscramble;
+ u32 res2[2];
+ u32 ana_cfg1;
+ u32 ana_cfg2;
+ u32 ana_cfg3;
+ u32 pll_cfg1;
+ u32 pll_cfg2;
+ u32 pll_cfg3;
+ u32 ana_sts;
+};
+
+struct sunxi_dw_hdmi_phy_priv {
+ void *base;
+ struct clk clk_bus;
+ struct clk clk_mod;
+ uint rcal;
+ struct reset_ctl reset;
+};
+
+void sunxi_dw_hdmi_phy_set(struct phy *_phy, const struct display_timing *edid,
+ int clk_div)
+{
+ struct sunxi_dw_hdmi_phy_priv *priv = dev_get_priv(_phy->dev);
+ struct sunxi_hdmi_phy *phy = priv->base;
+ u32 pll_cfg1, pll_cfg2, ana_cfg1, ana_cfg2, ana_cfg3;
+ u32 tmp, b_offset = 0;
+
+ /* bandwidth / frequency independent settings */
+
+ pll_cfg1 = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
+ SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
+ SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
+ SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
+ SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
+ SUN8I_HDMI_PHY_PLL_CFG1_CS |
+ SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
+ SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
+ SUN8I_HDMI_PHY_PLL_CFG1_BWS;
+
+ pll_cfg2 = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
+ SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
+ SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
+
+ ana_cfg1 = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
+ SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
+ SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
+ SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
+ SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
+ SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
+ SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
+ SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
+ SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
+ SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
+
+ ana_cfg2 = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
+
+ ana_cfg3 = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
+ SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
+ SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
+
+ /* bandwidth / frequency dependent settings */
+ if (edid->pixelclock.typ <= 27000000) {
+ pll_cfg1 |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
+ SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
+ pll_cfg2 |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
+ SUN8I_HDMI_PHY_PLL_CFG2_S(4);
+ ana_cfg1 |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
+ ana_cfg2 |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(priv->rcal);
+ ana_cfg3 |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
+ SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
+ } else if (edid->pixelclock.typ <= 74250000) {
+ pll_cfg1 |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
+ SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
+ pll_cfg2 |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
+ SUN8I_HDMI_PHY_PLL_CFG2_S(5);
+ ana_cfg1 |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
+ ana_cfg2 |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(priv->rcal);
+ ana_cfg3 |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
+ SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
+ } else if (edid->pixelclock.typ <= 148500000) {
+ pll_cfg1 |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
+ SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
+ pll_cfg2 |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
+ SUN8I_HDMI_PHY_PLL_CFG2_S(6);
+ ana_cfg2 |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
+ ana_cfg3 |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
+ SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
+ } else {
+ b_offset = 2;
+ pll_cfg1 |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
+ pll_cfg2 |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
+ SUN8I_HDMI_PHY_PLL_CFG2_S(7);
+ ana_cfg2 |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
+ SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
+ ana_cfg3 |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
+ SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
+ SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
+ }
+
+ writel(pll_cfg1, &phy->pll_cfg1);
+ writel(pll_cfg2 | (clk_div - 1), &phy->pll_cfg2);
+ mdelay(10);
+ writel(SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2, &phy->pll_cfg3);
+ setbits_le32(&phy->pll_cfg1, SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
+ mdelay(100);
+ tmp = readl(&phy->ana_sts) & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK;
+ tmp >>= SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
+ tmp = min(tmp + b_offset, (u32)0x3f);
+ setbits_le32(&phy->pll_cfg1,
+ SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
+ SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
+ setbits_le32(&phy->pll_cfg1, tmp);
+ mdelay(100);
+ writel(ana_cfg1, &phy->ana_cfg1);
+ writel(ana_cfg2, &phy->ana_cfg2);
+ writel(ana_cfg3, &phy->ana_cfg3);
+
+ if (edid->flags & DISPLAY_FLAGS_VSYNC_LOW)
+ setbits_le32(&phy->dbg_ctrl,
+ SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC);
+
+ if (edid->flags & DISPLAY_FLAGS_HSYNC_LOW)
+ setbits_le32(&phy->dbg_ctrl,
+ SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC);
+
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL);
+}
+
+static int sunxi_dw_hdmi_phy_init(struct phy *phy)
+{
+ struct sunxi_dw_hdmi_phy_priv *priv = dev_get_priv(phy->dev);
+ int ret;
+
+ ret = reset_deassert(&priv->reset);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&priv->clk_bus);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&priv->clk_mod);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sunxi_dw_hdmi_phy_power_on(struct phy *_phy)
+{
+ struct sunxi_dw_hdmi_phy_priv *priv = dev_get_priv(_phy->dev);
+ struct sunxi_hdmi_phy *phy = priv->base;
+ unsigned long tmo;
+
+ /* enable read access to HDMI controller */
+ writel(SUN8I_HDMI_PHY_READ_EN_MAGIC, &phy->read_en);
+ /* descramble register offsets */
+ writel(SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC, &phy->unscramble);
+
+ writel(0, &phy->ana_cfg1);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
+ udelay(5);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
+ udelay(10);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
+ udelay(5);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
+ udelay(40);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
+ udelay(100);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
+ setbits_le32(&phy->ana_cfg1,
+ SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
+ SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
+
+ /* Note that Allwinner code doesn't fail in case of timeout */
+ tmo = timer_get_us() + 2000;
+ while ((readl(&phy->ana_sts) & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D) == 0) {
+ if (timer_get_us() > tmo) {
+ printf("Warning: HDMI PHY init timeout!\n");
+ break;
+ }
+ }
+
+ setbits_le32(&phy->ana_cfg1,
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
+ SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
+ setbits_le32(&phy->ana_cfg1, SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
+
+ /* enable DDC communication */
+ writel(SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
+ SUN8I_HDMI_PHY_ANA_CFG3_SDAEN, &phy->ana_cfg3);
+
+ priv->rcal = readl(&phy->ana_sts) & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK;
+ priv->rcal >>= 2;
+
+ return 0;
+}
+
+/*
+ * This callback is abused for executing code after last register in
+ * controller is set.
+ */
+static int sunxi_dw_hdmi_phy_exit(struct phy *_phy)
+{
+ struct sunxi_dw_hdmi_phy_priv *priv = dev_get_priv(_phy->dev);
+ struct sunxi_hdmi_phy *phy = priv->base;
+
+ writel(0, &phy->unscramble);
+
+ return 0;
+}
+
+static struct phy_ops sunxi_dw_hdmi_phy_ops = {
+ .init = sunxi_dw_hdmi_phy_init,
+ .power_on = sunxi_dw_hdmi_phy_power_on,
+ .exit = sunxi_dw_hdmi_phy_exit,
+};
+
+static int sunxi_dw_hdmi_phy_probe(struct udevice *dev)
+{
+ struct sunxi_dw_hdmi_phy_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ ret = reset_get_by_index(dev, 0, &priv->reset);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_name(dev, "bus", &priv->clk_bus);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_name(dev, "mod", &priv->clk_mod);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct udevice_id sunxi_dw_hdmi_phy_ids[] = {
+ { .compatible = "allwinner,sun8i-h3-hdmi-phy" },
+ { .compatible = "allwinner,sun50i-a64-hdmi-phy" },
+ { }
+};
+
+U_BOOT_DRIVER(sunxi_dw_hdmi_phy) = {
+ .name = "sunxi_dw_hdmi_phy",
+ .id = UCLASS_PHY,
+ .of_match = sunxi_dw_hdmi_phy_ids,
+ .ops = &sunxi_dw_hdmi_phy_ops,
+ .probe = sunxi_dw_hdmi_phy_probe,
+ .priv_auto = sizeof(struct sunxi_dw_hdmi_phy_priv),
+};
diff --git a/drivers/video/sunxi/sunxi_dw_hdmi_phy.h b/drivers/video/sunxi/sunxi_dw_hdmi_phy.h
new file mode 100644
index 000000000000..78f85a413aaf
--- /dev/null
+++ b/drivers/video/sunxi/sunxi_dw_hdmi_phy.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2021 Jernej Skrabec <jernej.skrabec at siol.net>
+ */
+
+#ifndef __SUNXI_DW_HDMI_PHY_H
+#define __SUNXI_DW_HDMI_PHY_H
+
+#include <edid.h>
+
+/**
+ * sunxi_dw_hdmi_phy_set() - configure sunxi DW HDMI PHY
+ *
+ * Configure Sunxi DW HDMI PHY as found in A64, H3, H5 and other SoCs according
+ * to speficied mode.
+ *
+ * @phy: PHY port
+ * @edid: timing to configure
+ * @clk_div: Clock divider to set
+ */
+void sunxi_dw_hdmi_phy_set(struct phy *phy, const struct display_timing *edid,
+ int clk_div);
+
+#endif
--
2.30.1
More information about the U-Boot
mailing list