1b7c7436aSJernej Skrabec // SPDX-License-Identifier: GPL-2.0+
2b7c7436aSJernej Skrabec /*
3b7c7436aSJernej Skrabec  * Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net>
4b7c7436aSJernej Skrabec  */
5b7c7436aSJernej Skrabec 
64f86e817SJernej Skrabec #include <linux/delay.h>
7*722d4f06SRob Herring #include <linux/of.h>
89bf37977SSaravana Kannan #include <linux/of_platform.h>
9*722d4f06SRob Herring #include <linux/platform_device.h>
10b7c7436aSJernej Skrabec 
11b7c7436aSJernej Skrabec #include "sun8i_dw_hdmi.h"
12b7c7436aSJernej Skrabec 
13b7c7436aSJernej Skrabec /*
14b7c7436aSJernej Skrabec  * Address can be actually any value. Here is set to same value as
15b7c7436aSJernej Skrabec  * it is set in BSP driver.
16b7c7436aSJernej Skrabec  */
17b7c7436aSJernej Skrabec #define I2C_ADDR	0x69
18b7c7436aSJernej Skrabec 
190fb4b858SJernej Skrabec static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
200fb4b858SJernej Skrabec 	{
210fb4b858SJernej Skrabec 		30666000, {
220fb4b858SJernej Skrabec 			{ 0x00b3, 0x0000 },
230fb4b858SJernej Skrabec 			{ 0x2153, 0x0000 },
240fb4b858SJernej Skrabec 			{ 0x40f3, 0x0000 },
250fb4b858SJernej Skrabec 		},
260fb4b858SJernej Skrabec 	},  {
270fb4b858SJernej Skrabec 		36800000, {
280fb4b858SJernej Skrabec 			{ 0x00b3, 0x0000 },
290fb4b858SJernej Skrabec 			{ 0x2153, 0x0000 },
300fb4b858SJernej Skrabec 			{ 0x40a2, 0x0001 },
310fb4b858SJernej Skrabec 		},
320fb4b858SJernej Skrabec 	},  {
330fb4b858SJernej Skrabec 		46000000, {
340fb4b858SJernej Skrabec 			{ 0x00b3, 0x0000 },
350fb4b858SJernej Skrabec 			{ 0x2142, 0x0001 },
360fb4b858SJernej Skrabec 			{ 0x40a2, 0x0001 },
370fb4b858SJernej Skrabec 		},
380fb4b858SJernej Skrabec 	},  {
390fb4b858SJernej Skrabec 		61333000, {
400fb4b858SJernej Skrabec 			{ 0x0072, 0x0001 },
410fb4b858SJernej Skrabec 			{ 0x2142, 0x0001 },
420fb4b858SJernej Skrabec 			{ 0x40a2, 0x0001 },
430fb4b858SJernej Skrabec 		},
440fb4b858SJernej Skrabec 	},  {
450fb4b858SJernej Skrabec 		73600000, {
460fb4b858SJernej Skrabec 			{ 0x0072, 0x0001 },
470fb4b858SJernej Skrabec 			{ 0x2142, 0x0001 },
480fb4b858SJernej Skrabec 			{ 0x4061, 0x0002 },
490fb4b858SJernej Skrabec 		},
500fb4b858SJernej Skrabec 	},  {
510fb4b858SJernej Skrabec 		92000000, {
520fb4b858SJernej Skrabec 			{ 0x0072, 0x0001 },
530fb4b858SJernej Skrabec 			{ 0x2145, 0x0002 },
540fb4b858SJernej Skrabec 			{ 0x4061, 0x0002 },
550fb4b858SJernej Skrabec 		},
560fb4b858SJernej Skrabec 	},  {
570fb4b858SJernej Skrabec 		122666000, {
580fb4b858SJernej Skrabec 			{ 0x0051, 0x0002 },
590fb4b858SJernej Skrabec 			{ 0x2145, 0x0002 },
600fb4b858SJernej Skrabec 			{ 0x4061, 0x0002 },
610fb4b858SJernej Skrabec 		},
620fb4b858SJernej Skrabec 	},  {
630fb4b858SJernej Skrabec 		147200000, {
640fb4b858SJernej Skrabec 			{ 0x0051, 0x0002 },
650fb4b858SJernej Skrabec 			{ 0x2145, 0x0002 },
660fb4b858SJernej Skrabec 			{ 0x4064, 0x0003 },
670fb4b858SJernej Skrabec 		},
680fb4b858SJernej Skrabec 	},  {
690fb4b858SJernej Skrabec 		184000000, {
700fb4b858SJernej Skrabec 			{ 0x0051, 0x0002 },
710fb4b858SJernej Skrabec 			{ 0x214c, 0x0003 },
720fb4b858SJernej Skrabec 			{ 0x4064, 0x0003 },
730fb4b858SJernej Skrabec 		},
740fb4b858SJernej Skrabec 	},  {
750fb4b858SJernej Skrabec 		226666000, {
760fb4b858SJernej Skrabec 			{ 0x0040, 0x0003 },
770fb4b858SJernej Skrabec 			{ 0x214c, 0x0003 },
780fb4b858SJernej Skrabec 			{ 0x4064, 0x0003 },
790fb4b858SJernej Skrabec 		},
800fb4b858SJernej Skrabec 	},  {
810fb4b858SJernej Skrabec 		272000000, {
820fb4b858SJernej Skrabec 			{ 0x0040, 0x0003 },
830fb4b858SJernej Skrabec 			{ 0x214c, 0x0003 },
840fb4b858SJernej Skrabec 			{ 0x5a64, 0x0003 },
850fb4b858SJernej Skrabec 		},
860fb4b858SJernej Skrabec 	},  {
870fb4b858SJernej Skrabec 		340000000, {
880fb4b858SJernej Skrabec 			{ 0x0040, 0x0003 },
890fb4b858SJernej Skrabec 			{ 0x3b4c, 0x0003 },
900fb4b858SJernej Skrabec 			{ 0x5a64, 0x0003 },
910fb4b858SJernej Skrabec 		},
920fb4b858SJernej Skrabec 	},  {
930fb4b858SJernej Skrabec 		594000000, {
940fb4b858SJernej Skrabec 			{ 0x1a40, 0x0003 },
950fb4b858SJernej Skrabec 			{ 0x3b4c, 0x0003 },
960fb4b858SJernej Skrabec 			{ 0x5a64, 0x0003 },
970fb4b858SJernej Skrabec 		},
980fb4b858SJernej Skrabec 	}, {
990fb4b858SJernej Skrabec 		~0UL, {
1000fb4b858SJernej Skrabec 			{ 0x0000, 0x0000 },
1010fb4b858SJernej Skrabec 			{ 0x0000, 0x0000 },
1020fb4b858SJernej Skrabec 			{ 0x0000, 0x0000 },
1030fb4b858SJernej Skrabec 		},
1040fb4b858SJernej Skrabec 	}
1050fb4b858SJernej Skrabec };
1060fb4b858SJernej Skrabec 
1070fb4b858SJernej Skrabec static const struct dw_hdmi_curr_ctrl sun50i_h6_cur_ctr[] = {
1080fb4b858SJernej Skrabec 	/* pixelclk    bpp8    bpp10   bpp12 */
1090fb4b858SJernej Skrabec 	{ 27000000,  { 0x0012, 0x0000, 0x0000 }, },
1106a155216SJernej Skrabec 	{ 74250000,  { 0x0013, 0x001a, 0x001b }, },
1116a155216SJernej Skrabec 	{ 148500000, { 0x0019, 0x0033, 0x0034 }, },
1126a155216SJernej Skrabec 	{ 297000000, { 0x0019, 0x001b, 0x001b }, },
1136a155216SJernej Skrabec 	{ 594000000, { 0x0010, 0x001b, 0x001b }, },
1140fb4b858SJernej Skrabec 	{ ~0UL,      { 0x0000, 0x0000, 0x0000 }, }
1150fb4b858SJernej Skrabec };
1160fb4b858SJernej Skrabec 
1170fb4b858SJernej Skrabec static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = {
1180fb4b858SJernej Skrabec 	/*pixelclk   symbol   term   vlev*/
1196a155216SJernej Skrabec 	{ 27000000,  0x8009, 0x0007, 0x02b0 },
1206a155216SJernej Skrabec 	{ 74250000,  0x8009, 0x0006, 0x022d },
1216a155216SJernej Skrabec 	{ 148500000, 0x8029, 0x0006, 0x0270 },
1226a155216SJernej Skrabec 	{ 297000000, 0x8039, 0x0005, 0x01ab },
1236a155216SJernej Skrabec 	{ 594000000, 0x8029, 0x0000, 0x008a },
1240fb4b858SJernej Skrabec 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
1250fb4b858SJernej Skrabec };
1260fb4b858SJernej Skrabec 
sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy * phy,const struct drm_display_mode * mode)127ef2731e4SSamuel Holland static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
1286da62368SSamuel Holland 					const struct drm_display_mode *mode)
1296da62368SSamuel Holland {
1306da62368SSamuel Holland 	u32 val = 0;
1316da62368SSamuel Holland 
1326da62368SSamuel Holland 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
1336da62368SSamuel Holland 		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
1346da62368SSamuel Holland 
1356da62368SSamuel Holland 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
1366da62368SSamuel Holland 		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
1376da62368SSamuel Holland 
1386da62368SSamuel Holland 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
1396da62368SSamuel Holland 			   SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
1406da62368SSamuel Holland };
141ef2731e4SSamuel Holland 
sun8i_a83t_hdmi_phy_config(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)142ef2731e4SSamuel Holland static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
143ef2731e4SSamuel Holland 				      const struct drm_display_info *display,
144ef2731e4SSamuel Holland 				      const struct drm_display_mode *mode)
145b7c7436aSJernej Skrabec {
146ef2731e4SSamuel Holland 	unsigned int clk_rate = mode->crtc_clock * 1000;
147ef2731e4SSamuel Holland 	struct sun8i_hdmi_phy *phy = data;
148ef2731e4SSamuel Holland 
149ef2731e4SSamuel Holland 	sun8i_hdmi_phy_set_polarity(phy, mode);
150ef2731e4SSamuel Holland 
151b7c7436aSJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
152b7c7436aSJernej Skrabec 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
153b7c7436aSJernej Skrabec 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
154b7c7436aSJernej Skrabec 
155b7c7436aSJernej Skrabec 	/* power down */
156b7c7436aSJernej Skrabec 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
157b7c7436aSJernej Skrabec 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
158b7c7436aSJernej Skrabec 
1598fb241e2SSandor Yu 	dw_hdmi_phy_gen2_reset(hdmi);
160b7c7436aSJernej Skrabec 
161b7c7436aSJernej Skrabec 	dw_hdmi_phy_gen2_pddq(hdmi, 0);
162b7c7436aSJernej Skrabec 
163b7c7436aSJernej Skrabec 	dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR);
164b7c7436aSJernej Skrabec 
165b7c7436aSJernej Skrabec 	/*
166b7c7436aSJernej Skrabec 	 * Values are taken from BSP HDMI driver. Although AW didn't
167b7c7436aSJernej Skrabec 	 * release any documentation, explanation of this values can
168b7c7436aSJernej Skrabec 	 * be found in i.MX 6Dual/6Quad Reference Manual.
169b7c7436aSJernej Skrabec 	 */
1706fd90310SJernej Skrabec 	if (clk_rate <= 27000000) {
171b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
172b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
173b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
174b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
175b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
176b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
1776fd90310SJernej Skrabec 	} else if (clk_rate <= 74250000) {
178b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
179b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
180b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
181b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
182b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
183b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
1846fd90310SJernej Skrabec 	} else if (clk_rate <= 148500000) {
185b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
186b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
187b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
188b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
189b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e);
190b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09);
191b7c7436aSJernej Skrabec 	} else {
192b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06);
193b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
194b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
195b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
196b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e);
197b7c7436aSJernej Skrabec 		dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09);
198b7c7436aSJernej Skrabec 	}
199b7c7436aSJernej Skrabec 
200b7c7436aSJernej Skrabec 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e);
201b7c7436aSJernej Skrabec 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);
202b7c7436aSJernej Skrabec 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17);
203b7c7436aSJernej Skrabec 
204b7c7436aSJernej Skrabec 	dw_hdmi_phy_gen2_txpwron(hdmi, 1);
205b7c7436aSJernej Skrabec 
206b7c7436aSJernej Skrabec 	return 0;
2074f86e817SJernej Skrabec }
2084f86e817SJernej Skrabec 
sun8i_a83t_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)2096da62368SSamuel Holland static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
2106da62368SSamuel Holland {
2116da62368SSamuel Holland 	struct sun8i_hdmi_phy *phy = data;
2126da62368SSamuel Holland 
2136da62368SSamuel Holland 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
2146da62368SSamuel Holland 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
2156da62368SSamuel Holland 
2166da62368SSamuel Holland 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
2176da62368SSamuel Holland 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
2186da62368SSamuel Holland }
2196da62368SSamuel Holland 
2206da62368SSamuel Holland static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
2216da62368SSamuel Holland 	.init		= sun8i_a83t_hdmi_phy_config,
2226da62368SSamuel Holland 	.disable	= sun8i_a83t_hdmi_phy_disable,
2236da62368SSamuel Holland 	.read_hpd	= dw_hdmi_phy_read_hpd,
2246da62368SSamuel Holland 	.update_hpd	= dw_hdmi_phy_update_hpd,
2256da62368SSamuel Holland 	.setup_hpd	= dw_hdmi_phy_setup_hpd,
2266da62368SSamuel Holland };
2276da62368SSamuel Holland 
sun8i_h3_hdmi_phy_config(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)228ef2731e4SSamuel Holland static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
229ef2731e4SSamuel Holland 				    const struct drm_display_info *display,
230ef2731e4SSamuel Holland 				    const struct drm_display_mode *mode)
2314f86e817SJernej Skrabec {
232ef2731e4SSamuel Holland 	unsigned int clk_rate = mode->crtc_clock * 1000;
233ef2731e4SSamuel Holland 	struct sun8i_hdmi_phy *phy = data;
2344f86e817SJernej Skrabec 	u32 pll_cfg1_init;
2354f86e817SJernej Skrabec 	u32 pll_cfg2_init;
2364f86e817SJernej Skrabec 	u32 ana_cfg1_end;
2374f86e817SJernej Skrabec 	u32 ana_cfg2_init;
2384f86e817SJernej Skrabec 	u32 ana_cfg3_init;
2394f86e817SJernej Skrabec 	u32 b_offset = 0;
2404f86e817SJernej Skrabec 	u32 val;
2414f86e817SJernej Skrabec 
242ef2731e4SSamuel Holland 	if (phy->variant->has_phy_clk)
243ef2731e4SSamuel Holland 		clk_set_rate(phy->clk_phy, clk_rate);
244ef2731e4SSamuel Holland 
245ef2731e4SSamuel Holland 	sun8i_hdmi_phy_set_polarity(phy, mode);
246ef2731e4SSamuel Holland 
2474f86e817SJernej Skrabec 	/* bandwidth / frequency independent settings */
2484f86e817SJernej Skrabec 
2494f86e817SJernej Skrabec 	pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
2504f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
2514f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
2524f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
2534f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
2544f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_CS |
2554f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
2564f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
2574f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG1_BWS;
2584f86e817SJernej Skrabec 
2594f86e817SJernej Skrabec 	pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
2604f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
2614f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
2624f86e817SJernej Skrabec 
2634f86e817SJernej Skrabec 	ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
2644f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
2654f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
2664f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
2674f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
2684f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
2694f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
2704f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
2714f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
2724f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK |
2734f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
2744f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
2754f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
2764f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
2774f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
2784f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
2794f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
2804f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
2814f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
2824f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
2834f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
2844f86e817SJernej Skrabec 		       SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
2854f86e817SJernej Skrabec 
2864f86e817SJernej Skrabec 	ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
2874f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
2884f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
2894f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
2904f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
2914f86e817SJernej Skrabec 
2924f86e817SJernej Skrabec 	ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
2934f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
2944f86e817SJernej Skrabec 			SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
2954f86e817SJernej Skrabec 
2964f86e817SJernej Skrabec 	/* bandwidth / frequency dependent settings */
2974f86e817SJernej Skrabec 	if (clk_rate <= 27000000) {
2984f86e817SJernej Skrabec 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
2994f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
3004f86e817SJernej Skrabec 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
3014f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG2_S(4);
3024f86e817SJernej Skrabec 		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
3034f86e817SJernej Skrabec 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
3044f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
3054f86e817SJernej Skrabec 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
3064f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
3074f86e817SJernej Skrabec 	} else if (clk_rate <= 74250000) {
3084f86e817SJernej Skrabec 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
3094f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
3104f86e817SJernej Skrabec 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
3114f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG2_S(5);
3124f86e817SJernej Skrabec 		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
3134f86e817SJernej Skrabec 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
3144f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
3154f86e817SJernej Skrabec 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
3164f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
3174f86e817SJernej Skrabec 	} else if (clk_rate <= 148500000) {
3184f86e817SJernej Skrabec 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
3194f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
3204f86e817SJernej Skrabec 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
3214f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG2_S(6);
3224f86e817SJernej Skrabec 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
3234f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
3244f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
3254f86e817SJernej Skrabec 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
3264f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
3274f86e817SJernej Skrabec 	} else {
3284f86e817SJernej Skrabec 		b_offset = 2;
3294f86e817SJernej Skrabec 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
3304f86e817SJernej Skrabec 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
3314f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_PLL_CFG2_S(7);
3324f86e817SJernej Skrabec 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
3334f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
3344f86e817SJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
3354f86e817SJernej Skrabec 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
336831adffbSJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
337831adffbSJernej Skrabec 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
3384f86e817SJernej Skrabec 	}
3394f86e817SJernej Skrabec 
3404f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
3414f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
3424f86e817SJernej Skrabec 
34309f380e3SJernej Skrabec 	/*
34409f380e3SJernej Skrabec 	 * NOTE: We have to be careful not to overwrite PHY parent
34509f380e3SJernej Skrabec 	 * clock selection bit and clock divider.
34609f380e3SJernej Skrabec 	 */
34709f380e3SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
34809f380e3SJernej Skrabec 			   (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
34909f380e3SJernej Skrabec 			   pll_cfg1_init);
3504f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
3514f86e817SJernej Skrabec 			   (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
3524f86e817SJernej Skrabec 			   pll_cfg2_init);
3534f86e817SJernej Skrabec 	usleep_range(10000, 15000);
3544f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG,
3554f86e817SJernej Skrabec 		     SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2);
3564f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
3574f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN,
3584f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
3594f86e817SJernej Skrabec 	msleep(100);
3604f86e817SJernej Skrabec 
3614f86e817SJernej Skrabec 	/* get B value */
3624f86e817SJernej Skrabec 	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
3634f86e817SJernej Skrabec 	val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >>
3644f86e817SJernej Skrabec 		SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
3654f86e817SJernej Skrabec 	val = min(val + b_offset, (u32)0x3f);
3664f86e817SJernej Skrabec 
3674f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
3684f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
3694f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD,
3704f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
3714f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
3724f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
3734f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK,
3744f86e817SJernej Skrabec 			   val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT);
3754f86e817SJernej Skrabec 	msleep(100);
3764f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end);
3774f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init);
3784f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init);
3794f86e817SJernej Skrabec 
3804f86e817SJernej Skrabec 	return 0;
3814f86e817SJernej Skrabec }
382b7c7436aSJernej Skrabec 
sun8i_h3_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)383ef2731e4SSamuel Holland static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
3844f86e817SJernej Skrabec {
385ef2731e4SSamuel Holland 	struct sun8i_hdmi_phy *phy = data;
386ef2731e4SSamuel Holland 
3874f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
3884f86e817SJernej Skrabec 		     SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
3894f86e817SJernej Skrabec 		     SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
3904f86e817SJernej Skrabec 		     SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
3914f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
3924f86e817SJernej Skrabec }
3934f86e817SJernej Skrabec 
394ef2731e4SSamuel Holland static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
395ef2731e4SSamuel Holland 	.init		= sun8i_h3_hdmi_phy_config,
396ef2731e4SSamuel Holland 	.disable	= sun8i_h3_hdmi_phy_disable,
397ef2731e4SSamuel Holland 	.read_hpd	= dw_hdmi_phy_read_hpd,
398ef2731e4SSamuel Holland 	.update_hpd	= dw_hdmi_phy_update_hpd,
399ef2731e4SSamuel Holland 	.setup_hpd	= dw_hdmi_phy_setup_hpd,
400b7c7436aSJernej Skrabec };
401b7c7436aSJernej Skrabec 
sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy * phy)402cd54074eSJernej Skrabec static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
403cd54074eSJernej Skrabec {
404cd54074eSJernej Skrabec 	/* enable read access to HDMI controller */
405cd54074eSJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
406cd54074eSJernej Skrabec 		     SUN8I_HDMI_PHY_READ_EN_MAGIC);
407cd54074eSJernej Skrabec 
408cd54074eSJernej Skrabec 	/* unscramble register offsets */
409cd54074eSJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
410cd54074eSJernej Skrabec 		     SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
411cd54074eSJernej Skrabec }
412cd54074eSJernej Skrabec 
sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy * phy)4130fb4b858SJernej Skrabec static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy)
4140fb4b858SJernej Skrabec {
4150fb4b858SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
4160fb4b858SJernej Skrabec 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
4170fb4b858SJernej Skrabec 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
4180fb4b858SJernej Skrabec 
4190fb4b858SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
4200fb4b858SJernej Skrabec 			   0xffff0000, 0x80c00000);
4210fb4b858SJernej Skrabec }
4220fb4b858SJernej Skrabec 
sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy * phy)4236fd90310SJernej Skrabec static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
424b7c7436aSJernej Skrabec {
425cd54074eSJernej Skrabec 	sun8i_hdmi_phy_unlock(phy);
426cd54074eSJernej Skrabec 
427b7c7436aSJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
428b7c7436aSJernej Skrabec 			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
429b7c7436aSJernej Skrabec 			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
430b7c7436aSJernej Skrabec 
431b7c7436aSJernej Skrabec 	/*
432b7c7436aSJernej Skrabec 	 * Set PHY I2C address. It must match to the address set by
433b7c7436aSJernej Skrabec 	 * dw_hdmi_phy_set_slave_addr().
434b7c7436aSJernej Skrabec 	 */
435b7c7436aSJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
436b7c7436aSJernej Skrabec 			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK,
437b7c7436aSJernej Skrabec 			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
438b7c7436aSJernej Skrabec }
439b7c7436aSJernej Skrabec 
sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy * phy)4404f86e817SJernej Skrabec static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
4414f86e817SJernej Skrabec {
4424f86e817SJernej Skrabec 	unsigned int val;
4434f86e817SJernej Skrabec 
444cd54074eSJernej Skrabec 	sun8i_hdmi_phy_unlock(phy);
445cd54074eSJernej Skrabec 
4464f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0);
4474f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4484f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI,
4494f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
4504f86e817SJernej Skrabec 	udelay(5);
4514f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4524f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN,
4534f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
4544f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4554f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS,
4564f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
4574f86e817SJernej Skrabec 	usleep_range(10, 20);
4584f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4594f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN,
4604f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
4614f86e817SJernej Skrabec 	udelay(5);
4624f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4634f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN,
4644f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
4654f86e817SJernej Skrabec 	usleep_range(40, 100);
4664f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4674f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL,
4684f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
4694f86e817SJernej Skrabec 	usleep_range(100, 200);
4704f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4714f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG,
4724f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
4734f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4744f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
4754f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
4764f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2,
4774f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
4784f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
4794f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
4804f86e817SJernej Skrabec 
4814f86e817SJernej Skrabec 	/* wait for calibration to finish */
4824f86e817SJernej Skrabec 	regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val,
4834f86e817SJernej Skrabec 				 (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D),
4844f86e817SJernej Skrabec 				 100, 2000);
4854f86e817SJernej Skrabec 
4864f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4874f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK,
4884f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
4894f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4904f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
4914f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
4924f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
4934f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK,
4944f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
4954f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
4964f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
4974f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
4984f86e817SJernej Skrabec 
4994f86e817SJernej Skrabec 	/* enable DDC communication */
5004f86e817SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG,
5014f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
5024f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN,
5034f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
5044f86e817SJernej Skrabec 			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN);
5054f86e817SJernej Skrabec 
50609f380e3SJernej Skrabec 	/* reset PHY PLL clock parent */
50709f380e3SJernej Skrabec 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
50809f380e3SJernej Skrabec 			   SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
50909f380e3SJernej Skrabec 
5104f86e817SJernej Skrabec 	/* set HW control of CEC pins */
5114f86e817SJernej Skrabec 	regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
5124f86e817SJernej Skrabec 
5134f86e817SJernej Skrabec 	/* read calibration data */
5144f86e817SJernej Skrabec 	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
5154f86e817SJernej Skrabec 	phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
5164f86e817SJernej Skrabec }
5174f86e817SJernej Skrabec 
sun8i_hdmi_phy_init(struct sun8i_hdmi_phy * phy)518c64c8e04SJernej Skrabec int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
5196fd90310SJernej Skrabec {
520c64c8e04SJernej Skrabec 	int ret;
521c64c8e04SJernej Skrabec 
522c64c8e04SJernej Skrabec 	ret = reset_control_deassert(phy->rst_phy);
523c64c8e04SJernej Skrabec 	if (ret) {
524c64c8e04SJernej Skrabec 		dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
525c64c8e04SJernej Skrabec 		return ret;
526c64c8e04SJernej Skrabec 	}
527c64c8e04SJernej Skrabec 
528c64c8e04SJernej Skrabec 	ret = clk_prepare_enable(phy->clk_bus);
529c64c8e04SJernej Skrabec 	if (ret) {
530c64c8e04SJernej Skrabec 		dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
531c64c8e04SJernej Skrabec 		goto err_assert_rst_phy;
532c64c8e04SJernej Skrabec 	}
533c64c8e04SJernej Skrabec 
534c64c8e04SJernej Skrabec 	ret = clk_prepare_enable(phy->clk_mod);
535c64c8e04SJernej Skrabec 	if (ret) {
536c64c8e04SJernej Skrabec 		dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
537c64c8e04SJernej Skrabec 		goto err_disable_clk_bus;
538c64c8e04SJernej Skrabec 	}
539c64c8e04SJernej Skrabec 
540c64c8e04SJernej Skrabec 	if (phy->variant->has_phy_clk) {
541c64c8e04SJernej Skrabec 		ret = sun8i_phy_clk_create(phy, phy->dev,
542c64c8e04SJernej Skrabec 					   phy->variant->has_second_pll);
543c64c8e04SJernej Skrabec 		if (ret) {
544c64c8e04SJernej Skrabec 			dev_err(phy->dev, "Couldn't create the PHY clock\n");
545c64c8e04SJernej Skrabec 			goto err_disable_clk_mod;
546c64c8e04SJernej Skrabec 		}
547c64c8e04SJernej Skrabec 
548c64c8e04SJernej Skrabec 		clk_prepare_enable(phy->clk_phy);
549c64c8e04SJernej Skrabec 	}
550c64c8e04SJernej Skrabec 
5516fd90310SJernej Skrabec 	phy->variant->phy_init(phy);
552c64c8e04SJernej Skrabec 
553c64c8e04SJernej Skrabec 	return 0;
554c64c8e04SJernej Skrabec 
555c64c8e04SJernej Skrabec err_disable_clk_mod:
556c64c8e04SJernej Skrabec 	clk_disable_unprepare(phy->clk_mod);
557c64c8e04SJernej Skrabec err_disable_clk_bus:
558c64c8e04SJernej Skrabec 	clk_disable_unprepare(phy->clk_bus);
559c64c8e04SJernej Skrabec err_assert_rst_phy:
560c64c8e04SJernej Skrabec 	reset_control_assert(phy->rst_phy);
561c64c8e04SJernej Skrabec 
562c64c8e04SJernej Skrabec 	return ret;
563c64c8e04SJernej Skrabec }
564c64c8e04SJernej Skrabec 
sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy * phy)565c64c8e04SJernej Skrabec void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
566c64c8e04SJernej Skrabec {
567c64c8e04SJernej Skrabec 	clk_disable_unprepare(phy->clk_mod);
568c64c8e04SJernej Skrabec 	clk_disable_unprepare(phy->clk_bus);
569c64c8e04SJernej Skrabec 	clk_disable_unprepare(phy->clk_phy);
570c64c8e04SJernej Skrabec 
571c64c8e04SJernej Skrabec 	reset_control_assert(phy->rst_phy);
5726fd90310SJernej Skrabec }
5736fd90310SJernej Skrabec 
sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy * phy,struct dw_hdmi_plat_data * plat_data)574c71c9b2fSJernej Skrabec void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
575c71c9b2fSJernej Skrabec 			    struct dw_hdmi_plat_data *plat_data)
576b7c7436aSJernej Skrabec {
5771a395a56SSamuel Holland 	const struct sun8i_hdmi_phy_variant *variant = phy->variant;
578c71c9b2fSJernej Skrabec 
579cdf3e5e1SSamuel Holland 	if (variant->phy_ops) {
580cdf3e5e1SSamuel Holland 		plat_data->phy_ops = variant->phy_ops;
581c71c9b2fSJernej Skrabec 		plat_data->phy_name = "sun8i_dw_hdmi_phy";
582c71c9b2fSJernej Skrabec 		plat_data->phy_data = phy;
583c71c9b2fSJernej Skrabec 	} else {
584c71c9b2fSJernej Skrabec 		plat_data->mpll_cfg = variant->mpll_cfg;
585c71c9b2fSJernej Skrabec 		plat_data->cur_ctr = variant->cur_ctr;
586c71c9b2fSJernej Skrabec 		plat_data->phy_config = variant->phy_cfg;
587c71c9b2fSJernej Skrabec 	}
588b7c7436aSJernej Skrabec }
589b7c7436aSJernej Skrabec 
590f13478c9SRikard Falkeborn static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
591b7c7436aSJernej Skrabec 	.reg_bits	= 32,
592b7c7436aSJernej Skrabec 	.val_bits	= 32,
593b7c7436aSJernej Skrabec 	.reg_stride	= 4,
5944f86e817SJernej Skrabec 	.max_register	= SUN8I_HDMI_PHY_CEC_REG,
595b7c7436aSJernej Skrabec 	.name		= "phy"
596b7c7436aSJernej Skrabec };
597b7c7436aSJernej Skrabec 
5986fd90310SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
599ef2731e4SSamuel Holland 	.phy_ops = &sun8i_a83t_hdmi_phy_ops,
6006fd90310SJernej Skrabec 	.phy_init = &sun8i_hdmi_phy_init_a83t,
6016fd90310SJernej Skrabec };
6026fd90310SJernej Skrabec 
6034f86e817SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
6044f86e817SJernej Skrabec 	.has_phy_clk = true,
605ef2731e4SSamuel Holland 	.phy_ops = &sun8i_h3_hdmi_phy_ops,
6064f86e817SJernej Skrabec 	.phy_init = &sun8i_hdmi_phy_init_h3,
6074f86e817SJernej Skrabec };
6084f86e817SJernej Skrabec 
6093536faefSIcenowy Zheng static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
6103536faefSIcenowy Zheng 	.has_phy_clk = true,
6113536faefSIcenowy Zheng 	.has_second_pll = true,
612ef2731e4SSamuel Holland 	.phy_ops = &sun8i_h3_hdmi_phy_ops,
6133536faefSIcenowy Zheng 	.phy_init = &sun8i_hdmi_phy_init_h3,
6143536faefSIcenowy Zheng };
6153536faefSIcenowy Zheng 
616058262b1SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
617058262b1SJernej Skrabec 	.has_phy_clk = true,
618ef2731e4SSamuel Holland 	.phy_ops = &sun8i_h3_hdmi_phy_ops,
619058262b1SJernej Skrabec 	.phy_init = &sun8i_hdmi_phy_init_h3,
620058262b1SJernej Skrabec };
621058262b1SJernej Skrabec 
6220fb4b858SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = {
6230fb4b858SJernej Skrabec 	.cur_ctr  = sun50i_h6_cur_ctr,
6240fb4b858SJernej Skrabec 	.mpll_cfg = sun50i_h6_mpll_cfg,
6250fb4b858SJernej Skrabec 	.phy_cfg  = sun50i_h6_phy_config,
6260fb4b858SJernej Skrabec 	.phy_init = &sun50i_hdmi_phy_init_h6,
6270fb4b858SJernej Skrabec };
6280fb4b858SJernej Skrabec 
629b7c7436aSJernej Skrabec static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
6306fd90310SJernej Skrabec 	{
6316fd90310SJernej Skrabec 		.compatible = "allwinner,sun8i-a83t-hdmi-phy",
6326fd90310SJernej Skrabec 		.data = &sun8i_a83t_hdmi_phy,
6336fd90310SJernej Skrabec 	},
6344f86e817SJernej Skrabec 	{
6354f86e817SJernej Skrabec 		.compatible = "allwinner,sun8i-h3-hdmi-phy",
6364f86e817SJernej Skrabec 		.data = &sun8i_h3_hdmi_phy,
6374f86e817SJernej Skrabec 	},
6383536faefSIcenowy Zheng 	{
6393536faefSIcenowy Zheng 		.compatible = "allwinner,sun8i-r40-hdmi-phy",
6403536faefSIcenowy Zheng 		.data = &sun8i_r40_hdmi_phy,
6413536faefSIcenowy Zheng 	},
642058262b1SJernej Skrabec 	{
643058262b1SJernej Skrabec 		.compatible = "allwinner,sun50i-a64-hdmi-phy",
644058262b1SJernej Skrabec 		.data = &sun50i_a64_hdmi_phy,
645058262b1SJernej Skrabec 	},
6460fb4b858SJernej Skrabec 	{
6470fb4b858SJernej Skrabec 		.compatible = "allwinner,sun50i-h6-hdmi-phy",
6480fb4b858SJernej Skrabec 		.data = &sun50i_h6_hdmi_phy,
6490fb4b858SJernej Skrabec 	},
650b7c7436aSJernej Skrabec 	{ /* sentinel */ }
651b7c7436aSJernej Skrabec };
652b7c7436aSJernej Skrabec 
sun8i_hdmi_phy_get(struct sun8i_dw_hdmi * hdmi,struct device_node * node)6539bf37977SSaravana Kannan int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
6549bf37977SSaravana Kannan {
6559bf37977SSaravana Kannan 	struct platform_device *pdev = of_find_device_by_node(node);
6569bf37977SSaravana Kannan 	struct sun8i_hdmi_phy *phy;
6579bf37977SSaravana Kannan 
6589bf37977SSaravana Kannan 	if (!pdev)
6599bf37977SSaravana Kannan 		return -EPROBE_DEFER;
6609bf37977SSaravana Kannan 
6619bf37977SSaravana Kannan 	phy = platform_get_drvdata(pdev);
662c71af3daSMiaoqian Lin 	if (!phy) {
663c71af3daSMiaoqian Lin 		put_device(&pdev->dev);
6649bf37977SSaravana Kannan 		return -EPROBE_DEFER;
665c71af3daSMiaoqian Lin 	}
6669bf37977SSaravana Kannan 
6679bf37977SSaravana Kannan 	hdmi->phy = phy;
6689bf37977SSaravana Kannan 
6699bf37977SSaravana Kannan 	put_device(&pdev->dev);
6709bf37977SSaravana Kannan 
6719bf37977SSaravana Kannan 	return 0;
6729bf37977SSaravana Kannan }
6739bf37977SSaravana Kannan 
sun8i_hdmi_phy_probe(struct platform_device * pdev)6749bf37977SSaravana Kannan static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
675b7c7436aSJernej Skrabec {
6769bf37977SSaravana Kannan 	struct device *dev = &pdev->dev;
677b7c7436aSJernej Skrabec 	struct sun8i_hdmi_phy *phy;
678b7c7436aSJernej Skrabec 	void __iomem *regs;
679b7c7436aSJernej Skrabec 
680b7c7436aSJernej Skrabec 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
681b7c7436aSJernej Skrabec 	if (!phy)
682b7c7436aSJernej Skrabec 		return -ENOMEM;
683b7c7436aSJernej Skrabec 
6841a395a56SSamuel Holland 	phy->variant = of_device_get_match_data(dev);
685c64c8e04SJernej Skrabec 	phy->dev = dev;
6866fd90310SJernej Skrabec 
687719216ebSSamuel Holland 	regs = devm_platform_ioremap_resource(pdev, 0);
68894c25fb1SSamuel Holland 	if (IS_ERR(regs))
68994c25fb1SSamuel Holland 		return dev_err_probe(dev, PTR_ERR(regs),
69094c25fb1SSamuel Holland 				     "Couldn't map the HDMI PHY registers\n");
691b7c7436aSJernej Skrabec 
692b7c7436aSJernej Skrabec 	phy->regs = devm_regmap_init_mmio(dev, regs,
693b7c7436aSJernej Skrabec 					  &sun8i_hdmi_phy_regmap_config);
69494c25fb1SSamuel Holland 	if (IS_ERR(phy->regs))
69594c25fb1SSamuel Holland 		return dev_err_probe(dev, PTR_ERR(phy->regs),
69694c25fb1SSamuel Holland 				     "Couldn't create the HDMI PHY regmap\n");
697b7c7436aSJernej Skrabec 
69894c25fb1SSamuel Holland 	phy->clk_bus = devm_clk_get(dev, "bus");
69994c25fb1SSamuel Holland 	if (IS_ERR(phy->clk_bus))
70094c25fb1SSamuel Holland 		return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
70194c25fb1SSamuel Holland 				     "Could not get bus clock\n");
702b7c7436aSJernej Skrabec 
70394c25fb1SSamuel Holland 	phy->clk_mod = devm_clk_get(dev, "mod");
70494c25fb1SSamuel Holland 	if (IS_ERR(phy->clk_mod))
70594c25fb1SSamuel Holland 		return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
70694c25fb1SSamuel Holland 				     "Could not get mod clock\n");
707b7c7436aSJernej Skrabec 
7084f86e817SJernej Skrabec 	if (phy->variant->has_phy_clk) {
70994c25fb1SSamuel Holland 		phy->clk_pll0 = devm_clk_get(dev, "pll-0");
71094c25fb1SSamuel Holland 		if (IS_ERR(phy->clk_pll0))
71194c25fb1SSamuel Holland 			return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
71294c25fb1SSamuel Holland 					     "Could not get pll-0 clock\n");
7134f86e817SJernej Skrabec 
714aef13fd8SJernej Skrabec 		if (phy->variant->has_second_pll) {
71594c25fb1SSamuel Holland 			phy->clk_pll1 = devm_clk_get(dev, "pll-1");
71694c25fb1SSamuel Holland 			if (IS_ERR(phy->clk_pll1))
71794c25fb1SSamuel Holland 				return dev_err_probe(dev, PTR_ERR(phy->clk_pll1),
71894c25fb1SSamuel Holland 						     "Could not get pll-1 clock\n");
719aef13fd8SJernej Skrabec 		}
7204f86e817SJernej Skrabec 	}
7214f86e817SJernej Skrabec 
72294c25fb1SSamuel Holland 	phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
72394c25fb1SSamuel Holland 	if (IS_ERR(phy->rst_phy))
72494c25fb1SSamuel Holland 		return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
72594c25fb1SSamuel Holland 				     "Could not get phy reset control\n");
726b7c7436aSJernej Skrabec 
7279bf37977SSaravana Kannan 	platform_set_drvdata(pdev, phy);
728b7c7436aSJernej Skrabec 
729b7c7436aSJernej Skrabec 	return 0;
730b7c7436aSJernej Skrabec }
7319bf37977SSaravana Kannan 
7329bf37977SSaravana Kannan struct platform_driver sun8i_hdmi_phy_driver = {
7339bf37977SSaravana Kannan 	.probe  = sun8i_hdmi_phy_probe,
7349bf37977SSaravana Kannan 	.driver = {
7359bf37977SSaravana Kannan 		.name = "sun8i-hdmi-phy",
7369bf37977SSaravana Kannan 		.of_match_table = sun8i_hdmi_phy_of_table,
7379bf37977SSaravana Kannan 	},
7389bf37977SSaravana Kannan };
739