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.skra...@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, w...@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.skra...@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.skra...@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