cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sun8i_hdmi_phy.c (22525B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net>
      4 */
      5
      6#include <linux/delay.h>
      7#include <linux/of_address.h>
      8#include <linux/of_platform.h>
      9
     10#include "sun8i_dw_hdmi.h"
     11
     12/*
     13 * Address can be actually any value. Here is set to same value as
     14 * it is set in BSP driver.
     15 */
     16#define I2C_ADDR	0x69
     17
     18static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
     19	{
     20		30666000, {
     21			{ 0x00b3, 0x0000 },
     22			{ 0x2153, 0x0000 },
     23			{ 0x40f3, 0x0000 },
     24		},
     25	},  {
     26		36800000, {
     27			{ 0x00b3, 0x0000 },
     28			{ 0x2153, 0x0000 },
     29			{ 0x40a2, 0x0001 },
     30		},
     31	},  {
     32		46000000, {
     33			{ 0x00b3, 0x0000 },
     34			{ 0x2142, 0x0001 },
     35			{ 0x40a2, 0x0001 },
     36		},
     37	},  {
     38		61333000, {
     39			{ 0x0072, 0x0001 },
     40			{ 0x2142, 0x0001 },
     41			{ 0x40a2, 0x0001 },
     42		},
     43	},  {
     44		73600000, {
     45			{ 0x0072, 0x0001 },
     46			{ 0x2142, 0x0001 },
     47			{ 0x4061, 0x0002 },
     48		},
     49	},  {
     50		92000000, {
     51			{ 0x0072, 0x0001 },
     52			{ 0x2145, 0x0002 },
     53			{ 0x4061, 0x0002 },
     54		},
     55	},  {
     56		122666000, {
     57			{ 0x0051, 0x0002 },
     58			{ 0x2145, 0x0002 },
     59			{ 0x4061, 0x0002 },
     60		},
     61	},  {
     62		147200000, {
     63			{ 0x0051, 0x0002 },
     64			{ 0x2145, 0x0002 },
     65			{ 0x4064, 0x0003 },
     66		},
     67	},  {
     68		184000000, {
     69			{ 0x0051, 0x0002 },
     70			{ 0x214c, 0x0003 },
     71			{ 0x4064, 0x0003 },
     72		},
     73	},  {
     74		226666000, {
     75			{ 0x0040, 0x0003 },
     76			{ 0x214c, 0x0003 },
     77			{ 0x4064, 0x0003 },
     78		},
     79	},  {
     80		272000000, {
     81			{ 0x0040, 0x0003 },
     82			{ 0x214c, 0x0003 },
     83			{ 0x5a64, 0x0003 },
     84		},
     85	},  {
     86		340000000, {
     87			{ 0x0040, 0x0003 },
     88			{ 0x3b4c, 0x0003 },
     89			{ 0x5a64, 0x0003 },
     90		},
     91	},  {
     92		594000000, {
     93			{ 0x1a40, 0x0003 },
     94			{ 0x3b4c, 0x0003 },
     95			{ 0x5a64, 0x0003 },
     96		},
     97	}, {
     98		~0UL, {
     99			{ 0x0000, 0x0000 },
    100			{ 0x0000, 0x0000 },
    101			{ 0x0000, 0x0000 },
    102		},
    103	}
    104};
    105
    106static const struct dw_hdmi_curr_ctrl sun50i_h6_cur_ctr[] = {
    107	/* pixelclk    bpp8    bpp10   bpp12 */
    108	{ 27000000,  { 0x0012, 0x0000, 0x0000 }, },
    109	{ 74250000,  { 0x0013, 0x001a, 0x001b }, },
    110	{ 148500000, { 0x0019, 0x0033, 0x0034 }, },
    111	{ 297000000, { 0x0019, 0x001b, 0x001b }, },
    112	{ 594000000, { 0x0010, 0x001b, 0x001b }, },
    113	{ ~0UL,      { 0x0000, 0x0000, 0x0000 }, }
    114};
    115
    116static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = {
    117	/*pixelclk   symbol   term   vlev*/
    118	{ 27000000,  0x8009, 0x0007, 0x02b0 },
    119	{ 74250000,  0x8009, 0x0006, 0x022d },
    120	{ 148500000, 0x8029, 0x0006, 0x0270 },
    121	{ 297000000, 0x8039, 0x0005, 0x01ab },
    122	{ 594000000, 0x8029, 0x0000, 0x008a },
    123	{ ~0UL,	     0x0000, 0x0000, 0x0000}
    124};
    125
    126static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
    127				      struct sun8i_hdmi_phy *phy,
    128				      unsigned int clk_rate)
    129{
    130	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
    131			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
    132			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
    133
    134	/* power down */
    135	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
    136	dw_hdmi_phy_gen2_pddq(hdmi, 1);
    137
    138	dw_hdmi_phy_gen2_reset(hdmi);
    139
    140	dw_hdmi_phy_gen2_pddq(hdmi, 0);
    141
    142	dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR);
    143
    144	/*
    145	 * Values are taken from BSP HDMI driver. Although AW didn't
    146	 * release any documentation, explanation of this values can
    147	 * be found in i.MX 6Dual/6Quad Reference Manual.
    148	 */
    149	if (clk_rate <= 27000000) {
    150		dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
    151		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
    152		dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
    153		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
    154		dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
    155		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
    156	} else if (clk_rate <= 74250000) {
    157		dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
    158		dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
    159		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
    160		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
    161		dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
    162		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
    163	} else if (clk_rate <= 148500000) {
    164		dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
    165		dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
    166		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
    167		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
    168		dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e);
    169		dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09);
    170	} else {
    171		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06);
    172		dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
    173		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
    174		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
    175		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e);
    176		dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09);
    177	}
    178
    179	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e);
    180	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);
    181	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17);
    182
    183	dw_hdmi_phy_gen2_txpwron(hdmi, 1);
    184
    185	return 0;
    186}
    187
    188static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
    189				    struct sun8i_hdmi_phy *phy,
    190				    unsigned int clk_rate)
    191{
    192	u32 pll_cfg1_init;
    193	u32 pll_cfg2_init;
    194	u32 ana_cfg1_end;
    195	u32 ana_cfg2_init;
    196	u32 ana_cfg3_init;
    197	u32 b_offset = 0;
    198	u32 val;
    199
    200	/* bandwidth / frequency independent settings */
    201
    202	pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
    203			SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
    204			SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
    205			SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
    206			SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
    207			SUN8I_HDMI_PHY_PLL_CFG1_CS |
    208			SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
    209			SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
    210			SUN8I_HDMI_PHY_PLL_CFG1_BWS;
    211
    212	pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
    213			SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
    214			SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
    215
    216	ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
    217		       SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
    218		       SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
    219		       SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
    220		       SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
    221		       SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
    222		       SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
    223		       SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
    224		       SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
    225		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK |
    226		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
    227		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
    228		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
    229		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
    230		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
    231		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
    232		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
    233		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
    234		       SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
    235		       SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
    236		       SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
    237		       SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
    238
    239	ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
    240			SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
    241			SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
    242			SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
    243			SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
    244
    245	ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
    246			SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
    247			SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
    248
    249	/* bandwidth / frequency dependent settings */
    250	if (clk_rate <= 27000000) {
    251		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
    252				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
    253		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
    254				 SUN8I_HDMI_PHY_PLL_CFG2_S(4);
    255		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
    256		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
    257				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
    258		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
    259				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
    260	} else if (clk_rate <= 74250000) {
    261		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
    262				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
    263		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
    264				 SUN8I_HDMI_PHY_PLL_CFG2_S(5);
    265		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
    266		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
    267				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
    268		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
    269				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
    270	} else if (clk_rate <= 148500000) {
    271		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
    272				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
    273		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
    274				 SUN8I_HDMI_PHY_PLL_CFG2_S(6);
    275		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
    276				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
    277				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
    278		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
    279				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
    280	} else {
    281		b_offset = 2;
    282		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
    283		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
    284				 SUN8I_HDMI_PHY_PLL_CFG2_S(7);
    285		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
    286				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
    287				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
    288		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
    289				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
    290				 SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
    291	}
    292
    293	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    294			   SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
    295
    296	/*
    297	 * NOTE: We have to be careful not to overwrite PHY parent
    298	 * clock selection bit and clock divider.
    299	 */
    300	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
    301			   (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
    302			   pll_cfg1_init);
    303	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
    304			   (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
    305			   pll_cfg2_init);
    306	usleep_range(10000, 15000);
    307	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG,
    308		     SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2);
    309	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
    310			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN,
    311			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
    312	msleep(100);
    313
    314	/* get B value */
    315	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
    316	val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >>
    317		SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
    318	val = min(val + b_offset, (u32)0x3f);
    319
    320	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
    321			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
    322			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD,
    323			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
    324			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
    325	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
    326			   SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK,
    327			   val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT);
    328	msleep(100);
    329	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end);
    330	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init);
    331	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init);
    332
    333	return 0;
    334}
    335
    336static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
    337				 const struct drm_display_info *display,
    338				 const struct drm_display_mode *mode)
    339{
    340	struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
    341	u32 val = 0;
    342
    343	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
    344		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
    345
    346	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
    347		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
    348
    349	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
    350			   SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
    351
    352	if (phy->variant->has_phy_clk)
    353		clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000);
    354
    355	return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
    356};
    357
    358static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi,
    359					struct sun8i_hdmi_phy *phy)
    360{
    361	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
    362	dw_hdmi_phy_gen2_pddq(hdmi, 1);
    363
    364	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
    365			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
    366}
    367
    368static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi,
    369				      struct sun8i_hdmi_phy *phy)
    370{
    371	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    372		     SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
    373		     SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
    374		     SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
    375	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
    376}
    377
    378static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
    379{
    380	struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
    381
    382	phy->variant->phy_disable(hdmi, phy);
    383}
    384
    385static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = {
    386	.init = &sun8i_hdmi_phy_config,
    387	.disable = &sun8i_hdmi_phy_disable,
    388	.read_hpd = &dw_hdmi_phy_read_hpd,
    389	.update_hpd = &dw_hdmi_phy_update_hpd,
    390	.setup_hpd = &dw_hdmi_phy_setup_hpd,
    391};
    392
    393static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
    394{
    395	/* enable read access to HDMI controller */
    396	regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
    397		     SUN8I_HDMI_PHY_READ_EN_MAGIC);
    398
    399	/* unscramble register offsets */
    400	regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
    401		     SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
    402}
    403
    404static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy)
    405{
    406	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
    407			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
    408			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
    409
    410	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
    411			   0xffff0000, 0x80c00000);
    412}
    413
    414static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
    415{
    416	sun8i_hdmi_phy_unlock(phy);
    417
    418	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
    419			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
    420			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
    421
    422	/*
    423	 * Set PHY I2C address. It must match to the address set by
    424	 * dw_hdmi_phy_set_slave_addr().
    425	 */
    426	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
    427			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK,
    428			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
    429}
    430
    431static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
    432{
    433	unsigned int val;
    434
    435	sun8i_hdmi_phy_unlock(phy);
    436
    437	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0);
    438	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    439			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI,
    440			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
    441	udelay(5);
    442	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    443			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN,
    444			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
    445	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    446			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS,
    447			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
    448	usleep_range(10, 20);
    449	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    450			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN,
    451			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
    452	udelay(5);
    453	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    454			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN,
    455			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
    456	usleep_range(40, 100);
    457	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    458			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL,
    459			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
    460	usleep_range(100, 200);
    461	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    462			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG,
    463			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
    464	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    465			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
    466			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
    467			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2,
    468			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
    469			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
    470			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
    471
    472	/* wait for calibration to finish */
    473	regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val,
    474				 (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D),
    475				 100, 2000);
    476
    477	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    478			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK,
    479			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
    480	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
    481			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
    482			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
    483			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
    484			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK,
    485			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
    486			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
    487			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
    488			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
    489
    490	/* enable DDC communication */
    491	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG,
    492			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
    493			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN,
    494			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
    495			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN);
    496
    497	/* reset PHY PLL clock parent */
    498	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
    499			   SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
    500
    501	/* set HW control of CEC pins */
    502	regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
    503
    504	/* read calibration data */
    505	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
    506	phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
    507}
    508
    509int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
    510{
    511	int ret;
    512
    513	ret = reset_control_deassert(phy->rst_phy);
    514	if (ret) {
    515		dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
    516		return ret;
    517	}
    518
    519	ret = clk_prepare_enable(phy->clk_bus);
    520	if (ret) {
    521		dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
    522		goto err_assert_rst_phy;
    523	}
    524
    525	ret = clk_prepare_enable(phy->clk_mod);
    526	if (ret) {
    527		dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
    528		goto err_disable_clk_bus;
    529	}
    530
    531	if (phy->variant->has_phy_clk) {
    532		ret = sun8i_phy_clk_create(phy, phy->dev,
    533					   phy->variant->has_second_pll);
    534		if (ret) {
    535			dev_err(phy->dev, "Couldn't create the PHY clock\n");
    536			goto err_disable_clk_mod;
    537		}
    538
    539		clk_prepare_enable(phy->clk_phy);
    540	}
    541
    542	phy->variant->phy_init(phy);
    543
    544	return 0;
    545
    546err_disable_clk_mod:
    547	clk_disable_unprepare(phy->clk_mod);
    548err_disable_clk_bus:
    549	clk_disable_unprepare(phy->clk_bus);
    550err_assert_rst_phy:
    551	reset_control_assert(phy->rst_phy);
    552
    553	return ret;
    554}
    555
    556void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
    557{
    558	clk_disable_unprepare(phy->clk_mod);
    559	clk_disable_unprepare(phy->clk_bus);
    560	clk_disable_unprepare(phy->clk_phy);
    561
    562	reset_control_assert(phy->rst_phy);
    563}
    564
    565void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
    566			    struct dw_hdmi_plat_data *plat_data)
    567{
    568	struct sun8i_hdmi_phy_variant *variant = phy->variant;
    569
    570	if (variant->is_custom_phy) {
    571		plat_data->phy_ops = &sun8i_hdmi_phy_ops;
    572		plat_data->phy_name = "sun8i_dw_hdmi_phy";
    573		plat_data->phy_data = phy;
    574	} else {
    575		plat_data->mpll_cfg = variant->mpll_cfg;
    576		plat_data->cur_ctr = variant->cur_ctr;
    577		plat_data->phy_config = variant->phy_cfg;
    578	}
    579}
    580
    581static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
    582	.reg_bits	= 32,
    583	.val_bits	= 32,
    584	.reg_stride	= 4,
    585	.max_register	= SUN8I_HDMI_PHY_CEC_REG,
    586	.name		= "phy"
    587};
    588
    589static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
    590	.is_custom_phy = true,
    591	.phy_init = &sun8i_hdmi_phy_init_a83t,
    592	.phy_disable = &sun8i_hdmi_phy_disable_a83t,
    593	.phy_config = &sun8i_hdmi_phy_config_a83t,
    594};
    595
    596static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
    597	.has_phy_clk = true,
    598	.is_custom_phy = true,
    599	.phy_init = &sun8i_hdmi_phy_init_h3,
    600	.phy_disable = &sun8i_hdmi_phy_disable_h3,
    601	.phy_config = &sun8i_hdmi_phy_config_h3,
    602};
    603
    604static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
    605	.has_phy_clk = true,
    606	.has_second_pll = true,
    607	.is_custom_phy = true,
    608	.phy_init = &sun8i_hdmi_phy_init_h3,
    609	.phy_disable = &sun8i_hdmi_phy_disable_h3,
    610	.phy_config = &sun8i_hdmi_phy_config_h3,
    611};
    612
    613static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
    614	.has_phy_clk = true,
    615	.is_custom_phy = true,
    616	.phy_init = &sun8i_hdmi_phy_init_h3,
    617	.phy_disable = &sun8i_hdmi_phy_disable_h3,
    618	.phy_config = &sun8i_hdmi_phy_config_h3,
    619};
    620
    621static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = {
    622	.cur_ctr  = sun50i_h6_cur_ctr,
    623	.mpll_cfg = sun50i_h6_mpll_cfg,
    624	.phy_cfg  = sun50i_h6_phy_config,
    625	.phy_init = &sun50i_hdmi_phy_init_h6,
    626};
    627
    628static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
    629	{
    630		.compatible = "allwinner,sun8i-a83t-hdmi-phy",
    631		.data = &sun8i_a83t_hdmi_phy,
    632	},
    633	{
    634		.compatible = "allwinner,sun8i-h3-hdmi-phy",
    635		.data = &sun8i_h3_hdmi_phy,
    636	},
    637	{
    638		.compatible = "allwinner,sun8i-r40-hdmi-phy",
    639		.data = &sun8i_r40_hdmi_phy,
    640	},
    641	{
    642		.compatible = "allwinner,sun50i-a64-hdmi-phy",
    643		.data = &sun50i_a64_hdmi_phy,
    644	},
    645	{
    646		.compatible = "allwinner,sun50i-h6-hdmi-phy",
    647		.data = &sun50i_h6_hdmi_phy,
    648	},
    649	{ /* sentinel */ }
    650};
    651
    652int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
    653{
    654	struct platform_device *pdev = of_find_device_by_node(node);
    655	struct sun8i_hdmi_phy *phy;
    656
    657	if (!pdev)
    658		return -EPROBE_DEFER;
    659
    660	phy = platform_get_drvdata(pdev);
    661	if (!phy) {
    662		put_device(&pdev->dev);
    663		return -EPROBE_DEFER;
    664	}
    665
    666	hdmi->phy = phy;
    667
    668	put_device(&pdev->dev);
    669
    670	return 0;
    671}
    672
    673static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
    674{
    675	const struct of_device_id *match;
    676	struct device *dev = &pdev->dev;
    677	struct device_node *node = dev->of_node;
    678	struct sun8i_hdmi_phy *phy;
    679	struct resource res;
    680	void __iomem *regs;
    681	int ret;
    682
    683	match = of_match_node(sun8i_hdmi_phy_of_table, node);
    684	if (!match) {
    685		dev_err(dev, "Incompatible HDMI PHY\n");
    686		return -EINVAL;
    687	}
    688
    689	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
    690	if (!phy)
    691		return -ENOMEM;
    692
    693	phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
    694	phy->dev = dev;
    695
    696	ret = of_address_to_resource(node, 0, &res);
    697	if (ret) {
    698		dev_err(dev, "phy: Couldn't get our resources\n");
    699		return ret;
    700	}
    701
    702	regs = devm_ioremap_resource(dev, &res);
    703	if (IS_ERR(regs)) {
    704		dev_err(dev, "Couldn't map the HDMI PHY registers\n");
    705		return PTR_ERR(regs);
    706	}
    707
    708	phy->regs = devm_regmap_init_mmio(dev, regs,
    709					  &sun8i_hdmi_phy_regmap_config);
    710	if (IS_ERR(phy->regs)) {
    711		dev_err(dev, "Couldn't create the HDMI PHY regmap\n");
    712		return PTR_ERR(phy->regs);
    713	}
    714
    715	phy->clk_bus = of_clk_get_by_name(node, "bus");
    716	if (IS_ERR(phy->clk_bus)) {
    717		dev_err(dev, "Could not get bus clock\n");
    718		return PTR_ERR(phy->clk_bus);
    719	}
    720
    721	phy->clk_mod = of_clk_get_by_name(node, "mod");
    722	if (IS_ERR(phy->clk_mod)) {
    723		dev_err(dev, "Could not get mod clock\n");
    724		ret = PTR_ERR(phy->clk_mod);
    725		goto err_put_clk_bus;
    726	}
    727
    728	if (phy->variant->has_phy_clk) {
    729		phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
    730		if (IS_ERR(phy->clk_pll0)) {
    731			dev_err(dev, "Could not get pll-0 clock\n");
    732			ret = PTR_ERR(phy->clk_pll0);
    733			goto err_put_clk_mod;
    734		}
    735
    736		if (phy->variant->has_second_pll) {
    737			phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
    738			if (IS_ERR(phy->clk_pll1)) {
    739				dev_err(dev, "Could not get pll-1 clock\n");
    740				ret = PTR_ERR(phy->clk_pll1);
    741				goto err_put_clk_pll0;
    742			}
    743		}
    744	}
    745
    746	phy->rst_phy = of_reset_control_get_shared(node, "phy");
    747	if (IS_ERR(phy->rst_phy)) {
    748		dev_err(dev, "Could not get phy reset control\n");
    749		ret = PTR_ERR(phy->rst_phy);
    750		goto err_put_clk_pll1;
    751	}
    752
    753	platform_set_drvdata(pdev, phy);
    754
    755	return 0;
    756
    757err_put_clk_pll1:
    758	clk_put(phy->clk_pll1);
    759err_put_clk_pll0:
    760	clk_put(phy->clk_pll0);
    761err_put_clk_mod:
    762	clk_put(phy->clk_mod);
    763err_put_clk_bus:
    764	clk_put(phy->clk_bus);
    765
    766	return ret;
    767}
    768
    769static int sun8i_hdmi_phy_remove(struct platform_device *pdev)
    770{
    771	struct sun8i_hdmi_phy *phy = platform_get_drvdata(pdev);
    772
    773	reset_control_put(phy->rst_phy);
    774
    775	clk_put(phy->clk_pll0);
    776	clk_put(phy->clk_pll1);
    777	clk_put(phy->clk_mod);
    778	clk_put(phy->clk_bus);
    779	return 0;
    780}
    781
    782struct platform_driver sun8i_hdmi_phy_driver = {
    783	.probe  = sun8i_hdmi_phy_probe,
    784	.remove = sun8i_hdmi_phy_remove,
    785	.driver = {
    786		.name = "sun8i-hdmi-phy",
    787		.of_match_table = sun8i_hdmi_phy_of_table,
    788	},
    789};