xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip-inno-hdmi-phy.c (revision 27c6606c3e1072ee241e4f6fdace157252b5157a)
18e2bab3fSAlgea Cao /*
28e2bab3fSAlgea Cao  * SPDX-License-Identifier:     GPL-2.0+
38e2bab3fSAlgea Cao  * (C) Copyright 2008-2016 Fuzhou Rockchip Electronics Co., Ltd
48e2bab3fSAlgea Cao  */
58e2bab3fSAlgea Cao 
619957ff8SAlgea Cao #include <clk-uclass.h>
78e2bab3fSAlgea Cao #include <config.h>
88e2bab3fSAlgea Cao #include <common.h>
98e2bab3fSAlgea Cao #include <errno.h>
108e2bab3fSAlgea Cao #include <malloc.h>
118e2bab3fSAlgea Cao #include <misc.h>
128e2bab3fSAlgea Cao #include <fdtdec.h>
138e2bab3fSAlgea Cao #include <fdt_support.h>
148e2bab3fSAlgea Cao #include <asm/unaligned.h>
1519957ff8SAlgea Cao #include <asm/arch/clock.h>
168e2bab3fSAlgea Cao #include <dm/device.h>
1719957ff8SAlgea Cao #include <dm/lists.h>
188e2bab3fSAlgea Cao #include <dm/read.h>
198e2bab3fSAlgea Cao #include <asm/io.h>
208e2bab3fSAlgea Cao #include <linux/list.h>
218e2bab3fSAlgea Cao #include <div64.h>
228e2bab3fSAlgea Cao #include <linux/media-bus-format.h>
238e2bab3fSAlgea Cao 
248e2bab3fSAlgea Cao #include "rockchip_display.h"
258e2bab3fSAlgea Cao #include "rockchip_crtc.h"
266a3f4548SSandy Huang #include "rockchip_connector.h"
278e2bab3fSAlgea Cao #include "rockchip_phy.h"
288e2bab3fSAlgea Cao 
298e2bab3fSAlgea Cao #define INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT	1000
301953e619SWyon Bi #define UPDATE(x, h, l)	(((x) << (l)) & GENMASK((h), (l)))
318e2bab3fSAlgea Cao 
328e2bab3fSAlgea Cao /* REG: 0x00 */
338e2bab3fSAlgea Cao #define PRE_PLL_REFCLK_SEL_MASK			BIT(0)
348e2bab3fSAlgea Cao #define PRE_PLL_REFCLK_SEL_PCLK			BIT(0)
358e2bab3fSAlgea Cao #define PRE_PLL_REFCLK_SEL_OSCCLK		0
368e2bab3fSAlgea Cao /* REG: 0x01 */
378e2bab3fSAlgea Cao #define BYPASS_RXSENSE_EN_MASK			BIT(2)
388e2bab3fSAlgea Cao #define BYPASS_RXSENSE_EN			BIT(2)
398e2bab3fSAlgea Cao #define BYPASS_PWRON_EN_MASK			BIT(1)
408e2bab3fSAlgea Cao #define BYPASS_PWRON_EN				BIT(1)
418e2bab3fSAlgea Cao #define BYPASS_PLLPD_EN_MASK			BIT(0)
428e2bab3fSAlgea Cao #define BYPASS_PLLPD_EN				BIT(0)
438e2bab3fSAlgea Cao /* REG: 0x02 */
448e2bab3fSAlgea Cao #define BYPASS_PDATA_EN_MASK			BIT(4)
458e2bab3fSAlgea Cao #define BYPASS_PDATA_EN				BIT(4)
468e2bab3fSAlgea Cao #define PDATAEN_MASK				BIT(0)
478e2bab3fSAlgea Cao #define PDATAEN_DISABLE				BIT(0)
488e2bab3fSAlgea Cao #define PDATAEN_ENABLE				0
498e2bab3fSAlgea Cao /* REG: 0x03 */
508e2bab3fSAlgea Cao #define BYPASS_AUTO_TERM_RES_CAL		BIT(7)
518e2bab3fSAlgea Cao #define AUDO_TERM_RES_CAL_SPEED_14_8(x)		UPDATE(x, 6, 0)
528e2bab3fSAlgea Cao /* REG: 0x04 */
538e2bab3fSAlgea Cao #define AUDO_TERM_RES_CAL_SPEED_7_0(x)		UPDATE(x, 7, 0)
548e2bab3fSAlgea Cao /* REG: 0xaa */
558e2bab3fSAlgea Cao #define POST_PLL_CTRL_MASK			BIT(0)
568e2bab3fSAlgea Cao #define POST_PLL_CTRL_MANUAL			BIT(0)
578e2bab3fSAlgea Cao /* REG: 0xe0 */
588e2bab3fSAlgea Cao #define POST_PLL_POWER_MASK			BIT(5)
598e2bab3fSAlgea Cao #define POST_PLL_POWER_DOWN			BIT(5)
608e2bab3fSAlgea Cao #define POST_PLL_POWER_UP			0
618e2bab3fSAlgea Cao #define PRE_PLL_POWER_MASK			BIT(4)
628e2bab3fSAlgea Cao #define PRE_PLL_POWER_DOWN			BIT(4)
638e2bab3fSAlgea Cao #define PRE_PLL_POWER_UP			0
648e2bab3fSAlgea Cao #define RXSENSE_CLK_CH_MASK			BIT(3)
658e2bab3fSAlgea Cao #define RXSENSE_CLK_CH_ENABLE			BIT(3)
668e2bab3fSAlgea Cao #define RXSENSE_DATA_CH2_MASK			BIT(2)
678e2bab3fSAlgea Cao #define RXSENSE_DATA_CH2_ENABLE			BIT(2)
688e2bab3fSAlgea Cao #define RXSENSE_DATA_CH1_MASK			BIT(1)
698e2bab3fSAlgea Cao #define RXSENSE_DATA_CH1_ENABLE			BIT(1)
708e2bab3fSAlgea Cao #define RXSENSE_DATA_CH0_MASK			BIT(0)
718e2bab3fSAlgea Cao #define RXSENSE_DATA_CH0_ENABLE			BIT(0)
728e2bab3fSAlgea Cao /* REG: 0xe1 */
738e2bab3fSAlgea Cao #define BANDGAP_MASK				BIT(4)
748e2bab3fSAlgea Cao #define BANDGAP_ENABLE				BIT(4)
758e2bab3fSAlgea Cao #define BANDGAP_DISABLE				0
768e2bab3fSAlgea Cao #define TMDS_DRIVER_MASK			GENMASK(3, 0)
778e2bab3fSAlgea Cao #define TMDS_DRIVER_ENABLE			UPDATE(0xf, 3, 0)
788e2bab3fSAlgea Cao #define TMDS_DRIVER_DISABLE			0
798e2bab3fSAlgea Cao /* REG: 0xe2 */
808e2bab3fSAlgea Cao #define PRE_PLL_FB_DIV_8_MASK			BIT(7)
818e2bab3fSAlgea Cao #define PRE_PLL_FB_DIV_8_SHIFT			7
828e2bab3fSAlgea Cao #define PRE_PLL_FB_DIV_8(x)			UPDATE(x, 7, 7)
838e2bab3fSAlgea Cao #define PCLK_VCO_DIV_5_MASK			BIT(5)
848e2bab3fSAlgea Cao #define PCLK_VCO_DIV_5_SHIFT			5
858e2bab3fSAlgea Cao #define PCLK_VCO_DIV_5(x)			UPDATE(x, 5, 5)
868e2bab3fSAlgea Cao #define PRE_PLL_PRE_DIV_MASK			GENMASK(4, 0)
878e2bab3fSAlgea Cao #define PRE_PLL_PRE_DIV(x)			UPDATE(x, 4, 0)
888e2bab3fSAlgea Cao /* REG: 0xe3 */
898e2bab3fSAlgea Cao #define PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
908e2bab3fSAlgea Cao /* REG: 0xe4 */
918e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_B_MASK			GENMASK(6, 5)
928e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_B_SHIFT		5
938e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
948e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_A_MASK			GENMASK(4, 0)
958e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_A_SHIFT		0
968e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
978e2bab3fSAlgea Cao /* REG: 0xe5 */
988e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_C_MASK			GENMASK(6, 5)
998e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_C_SHIFT		5
1008e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
1018e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_D_MASK			GENMASK(4, 0)
1028e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_D_SHIFT		0
1038e2bab3fSAlgea Cao #define PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
1048e2bab3fSAlgea Cao /* REG: 0xe6 */
1058e2bab3fSAlgea Cao #define PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(5, 4)
1068e2bab3fSAlgea Cao #define PRE_PLL_TMDSCLK_DIV_C(x)		UPDATE(x, 5, 4)
1078e2bab3fSAlgea Cao #define PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(3, 2)
1088e2bab3fSAlgea Cao #define PRE_PLL_TMDSCLK_DIV_A(x)		UPDATE(x, 3, 2)
1098e2bab3fSAlgea Cao #define PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(1, 0)
1108e2bab3fSAlgea Cao #define PRE_PLL_TMDSCLK_DIV_B(x)		UPDATE(x, 1, 0)
1118e2bab3fSAlgea Cao /* REG: 0xe8 */
1128e2bab3fSAlgea Cao #define PRE_PLL_LOCK_STATUS			BIT(0)
1138e2bab3fSAlgea Cao /* REG: 0xe9 */
1148e2bab3fSAlgea Cao #define POST_PLL_POST_DIV_EN_MASK		GENMASK(7, 6)
1158e2bab3fSAlgea Cao #define POST_PLL_POST_DIV_ENABLE		UPDATE(3, 7, 6)
1168e2bab3fSAlgea Cao #define POST_PLL_POST_DIV_DISABLE		0
1178e2bab3fSAlgea Cao #define POST_PLL_PRE_DIV_MASK			GENMASK(4, 0)
1188e2bab3fSAlgea Cao #define POST_PLL_PRE_DIV(x)			UPDATE(x, 4, 0)
1198e2bab3fSAlgea Cao /* REG: 0xea */
1208e2bab3fSAlgea Cao #define POST_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
1218e2bab3fSAlgea Cao /* REG: 0xeb */
1228e2bab3fSAlgea Cao #define POST_PLL_FB_DIV_8_MASK			BIT(7)
1238e2bab3fSAlgea Cao #define POST_PLL_FB_DIV_8(x)			UPDATE(x, 7, 7)
1248e2bab3fSAlgea Cao #define POST_PLL_POST_DIV_MASK			GENMASK(5, 4)
1258e2bab3fSAlgea Cao #define POST_PLL_POST_DIV(x)			UPDATE(x, 5, 4)
1268e2bab3fSAlgea Cao #define POST_PLL_LOCK_STATUS			BIT(0)
1278e2bab3fSAlgea Cao /* REG: 0xee */
1288e2bab3fSAlgea Cao #define TMDS_CH_TA_MASK				GENMASK(7, 4)
1298e2bab3fSAlgea Cao #define TMDS_CH_TA_ENABLE			UPDATE(0xf, 7, 4)
1308e2bab3fSAlgea Cao #define TMDS_CH_TA_DISABLE			0
1318e2bab3fSAlgea Cao /* REG: 0xef */
1328e2bab3fSAlgea Cao #define TMDS_CLK_CH_TA(x)			UPDATE(x, 7, 6)
1338e2bab3fSAlgea Cao #define TMDS_DATA_CH2_TA(x)			UPDATE(x, 5, 4)
1348e2bab3fSAlgea Cao #define TMDS_DATA_CH1_TA(x)			UPDATE(x, 3, 2)
1358e2bab3fSAlgea Cao #define TMDS_DATA_CH0_TA(x)			UPDATE(x, 1, 0)
1368e2bab3fSAlgea Cao /* REG: 0xf0 */
1378e2bab3fSAlgea Cao #define TMDS_DATA_CH2_PRE_EMPHASIS_MASK		GENMASK(5, 4)
1388e2bab3fSAlgea Cao #define TMDS_DATA_CH2_PRE_EMPHASIS(x)		UPDATE(x, 5, 4)
1398e2bab3fSAlgea Cao #define TMDS_DATA_CH1_PRE_EMPHASIS_MASK		GENMASK(3, 2)
1408e2bab3fSAlgea Cao #define TMDS_DATA_CH1_PRE_EMPHASIS(x)		UPDATE(x, 3, 2)
1418e2bab3fSAlgea Cao #define TMDS_DATA_CH0_PRE_EMPHASIS_MASK		GENMASK(1, 0)
1428e2bab3fSAlgea Cao #define TMDS_DATA_CH0_PRE_EMPHASIS(x)		UPDATE(x, 1, 0)
1438e2bab3fSAlgea Cao /* REG: 0xf1 */
1448e2bab3fSAlgea Cao #define TMDS_CLK_CH_OUTPUT_SWING(x)		UPDATE(x, 7, 4)
1458e2bab3fSAlgea Cao #define TMDS_DATA_CH2_OUTPUT_SWING(x)		UPDATE(x, 3, 0)
1468e2bab3fSAlgea Cao /* REG: 0xf2 */
1478e2bab3fSAlgea Cao #define TMDS_DATA_CH1_OUTPUT_SWING(x)		UPDATE(x, 7, 4)
1488e2bab3fSAlgea Cao #define TMDS_DATA_CH0_OUTPUT_SWING(x)		UPDATE(x, 3, 0)
1498e2bab3fSAlgea Cao 
1508e2bab3fSAlgea Cao enum inno_hdmi_phy_type {
1518e2bab3fSAlgea Cao 	INNO_HDMI_PHY_RK3228,
15219957ff8SAlgea Cao 	INNO_HDMI_PHY_RK3328,
15319957ff8SAlgea Cao 	INNO_HDMI_PHY_RK3528
1548e2bab3fSAlgea Cao };
1558e2bab3fSAlgea Cao 
1568e2bab3fSAlgea Cao struct inno_hdmi_phy_drv_data;
1578e2bab3fSAlgea Cao 
1588e2bab3fSAlgea Cao struct phy_config {
1598e2bab3fSAlgea Cao 	unsigned long	tmdsclock;
1608e2bab3fSAlgea Cao 	u8		regs[14];
1618e2bab3fSAlgea Cao };
1628e2bab3fSAlgea Cao 
1638e2bab3fSAlgea Cao struct inno_hdmi_phy {
16415081c50SWyon Bi 	struct udevice *dev;
1658e2bab3fSAlgea Cao 	ofnode node;
1668e2bab3fSAlgea Cao 	void *regs;
1678e2bab3fSAlgea Cao 
1688e2bab3fSAlgea Cao 	/* platform data */
1698e2bab3fSAlgea Cao 	const struct inno_hdmi_phy_drv_data *plat_data;
1708e2bab3fSAlgea Cao 	unsigned long pixclock;
1718e2bab3fSAlgea Cao 	u32 bus_width;
1728e2bab3fSAlgea Cao 	struct phy_config *phy_cfg;
1738e2bab3fSAlgea Cao };
1748e2bab3fSAlgea Cao 
1758e2bab3fSAlgea Cao struct pre_pll_config {
1768e2bab3fSAlgea Cao 	unsigned long pixclock;
1778e2bab3fSAlgea Cao 	unsigned long tmdsclock;
1788e2bab3fSAlgea Cao 	u8 prediv;
1798e2bab3fSAlgea Cao 	u16 fbdiv;
1808e2bab3fSAlgea Cao 	u8 tmds_div_a;
1818e2bab3fSAlgea Cao 	u8 tmds_div_b;
1828e2bab3fSAlgea Cao 	u8 tmds_div_c;
1838e2bab3fSAlgea Cao 	u8 pclk_div_a;
1848e2bab3fSAlgea Cao 	u8 pclk_div_b;
1858e2bab3fSAlgea Cao 	u8 pclk_div_c;
1868e2bab3fSAlgea Cao 	u8 pclk_div_d;
1878e2bab3fSAlgea Cao 	u8 vco_div_5_en;
1888e2bab3fSAlgea Cao 	u32 fracdiv;
1898e2bab3fSAlgea Cao };
1908e2bab3fSAlgea Cao 
1918e2bab3fSAlgea Cao struct post_pll_config {
1928e2bab3fSAlgea Cao 	unsigned long tmdsclock;
1938e2bab3fSAlgea Cao 	u8 prediv;
1948e2bab3fSAlgea Cao 	u16 fbdiv;
1958e2bab3fSAlgea Cao 	u8 postdiv;
1968e2bab3fSAlgea Cao 	u8 version;
1978e2bab3fSAlgea Cao };
1988e2bab3fSAlgea Cao 
1998e2bab3fSAlgea Cao struct inno_hdmi_phy_ops {
2008e2bab3fSAlgea Cao 	void (*init)(struct inno_hdmi_phy *inno);
2018e2bab3fSAlgea Cao 	int (*power_on)(struct inno_hdmi_phy *inno,
2028e2bab3fSAlgea Cao 			const struct post_pll_config *cfg,
2038e2bab3fSAlgea Cao 			const struct phy_config *phy_cfg);
2048e2bab3fSAlgea Cao 	void (*power_off)(struct inno_hdmi_phy *inno);
2058e2bab3fSAlgea Cao 	int (*pre_pll_update)(struct inno_hdmi_phy *inno,
2068e2bab3fSAlgea Cao 			      const struct pre_pll_config *cfg);
2078e2bab3fSAlgea Cao 	unsigned long (*recalc_rate)(struct inno_hdmi_phy *inno,
2088e2bab3fSAlgea Cao 				     unsigned long parent_rate);
2098e2bab3fSAlgea Cao };
2108e2bab3fSAlgea Cao 
2118e2bab3fSAlgea Cao struct inno_hdmi_phy_drv_data {
2128e2bab3fSAlgea Cao 	enum inno_hdmi_phy_type		dev_type;
2138e2bab3fSAlgea Cao 	const struct inno_hdmi_phy_ops	*ops;
2148e2bab3fSAlgea Cao 	const struct phy_config		*phy_cfg_table;
2158e2bab3fSAlgea Cao };
2168e2bab3fSAlgea Cao 
2178e2bab3fSAlgea Cao struct rockchip_inno_data {
2188e2bab3fSAlgea Cao 	char compatible[30];
2198e2bab3fSAlgea Cao 	const void *data;
2208e2bab3fSAlgea Cao };
2218e2bab3fSAlgea Cao 
22219957ff8SAlgea Cao struct clk_inno_hdmi {
22319957ff8SAlgea Cao 	struct udevice *dev;
22419957ff8SAlgea Cao 	ulong rate;
22519957ff8SAlgea Cao };
22619957ff8SAlgea Cao 
22719957ff8SAlgea Cao /* global variables are used to pass reource from phy drivers to clk driver */
22819957ff8SAlgea Cao static struct inno_hdmi_phy *g_inno;
22919957ff8SAlgea Cao 
2308e2bab3fSAlgea Cao static const struct pre_pll_config pre_pll_cfg_table[] = {
2318e2bab3fSAlgea Cao 	{ 27000000,  27000000, 1,  90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
2328e2bab3fSAlgea Cao 	{ 27000000,  33750000, 1,  90, 1, 3, 3, 10, 3, 3, 4, 0, 0},
2338e2bab3fSAlgea Cao 	{ 40000000,  40000000, 1,  80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
2348e2bab3fSAlgea Cao 	{ 59341000,  59341000, 1,  98, 3, 1, 2,  1, 3, 3, 4, 0, 0xE6AE6B},
2358e2bab3fSAlgea Cao 	{ 59400000,  59400000, 1,  99, 3, 1, 1,  1, 3, 3, 4, 0, 0},
2368e2bab3fSAlgea Cao 	{ 59341000,  74176250, 1,  98, 0, 3, 3,  1, 3, 3, 4, 0, 0xE6AE6B},
2378e2bab3fSAlgea Cao 	{ 59400000,  74250000, 1,  99, 1, 2, 2,  1, 3, 3, 4, 0, 0},
2388e2bab3fSAlgea Cao 	{ 74176000,  74176000, 1,  98, 1, 2, 2,  1, 2, 3, 4, 0, 0xE6AE6B},
2398e2bab3fSAlgea Cao 	{ 74250000,  74250000, 1,  99, 1, 2, 2,  1, 2, 3, 4, 0, 0},
2408e2bab3fSAlgea Cao 	{ 74176000,  92720000, 4, 494, 1, 2, 2,  1, 3, 3, 4, 0, 0x816817},
2418e2bab3fSAlgea Cao 	{ 74250000,  92812500, 4, 495, 1, 2, 2,  1, 3, 3, 4, 0, 0},
2428e2bab3fSAlgea Cao 	{148352000, 148352000, 1,  98, 1, 1, 1,  1, 2, 2, 2, 0, 0xE6AE6B},
2438e2bab3fSAlgea Cao 	{148500000, 148500000, 1,  99, 1, 1, 1,  1, 2, 2, 2, 0, 0},
2448e2bab3fSAlgea Cao 	{148352000, 185440000, 4, 494, 0, 2, 2,  1, 3, 2, 2, 0, 0x816817},
2458e2bab3fSAlgea Cao 	{148500000, 185625000, 4, 495, 0, 2, 2,  1, 3, 2, 2, 0, 0},
2468e2bab3fSAlgea Cao 	{296703000, 296703000, 1,  98, 0, 1, 1,  1, 0, 2, 2, 0, 0xE6AE6B},
2478e2bab3fSAlgea Cao 	{297000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2, 2, 0, 0},
2488e2bab3fSAlgea Cao 	{296703000, 370878750, 4, 494, 1, 2, 0,  1, 3, 1, 1, 0, 0x816817},
2498e2bab3fSAlgea Cao 	{297000000, 371250000, 4, 495, 1, 2, 0,  1, 3, 1, 1, 0, 0},
2508e2bab3fSAlgea Cao 	{593407000, 296703500, 1,  98, 0, 1, 1,  1, 0, 2, 1, 0, 0xE6AE6B},
2518e2bab3fSAlgea Cao 	{594000000, 297000000, 1,  99, 0, 1, 1,  1, 0, 2, 1, 0, 0},
2528e2bab3fSAlgea Cao 	{593407000, 370879375, 4, 494, 1, 2, 0,  1, 3, 1, 1, 1, 0x816817},
2538e2bab3fSAlgea Cao 	{594000000, 371250000, 4, 495, 1, 2, 0,  1, 3, 1, 1, 1, 0},
2548e2bab3fSAlgea Cao 	{593407000, 593407000, 1,  98, 0, 2, 0,  1, 0, 1, 1, 0, 0xE6AE6B},
2558e2bab3fSAlgea Cao 	{594000000, 594000000, 1,  99, 0, 2, 0,  1, 0, 1, 1, 0, 0},
2568e2bab3fSAlgea Cao 	{     ~0UL,	    0, 0,   0, 0, 0, 0,  0, 0, 0, 0, 0, 0}
2578e2bab3fSAlgea Cao };
2588e2bab3fSAlgea Cao 
2598e2bab3fSAlgea Cao static const struct post_pll_config post_pll_cfg_table[] = {
2608e2bab3fSAlgea Cao 	{33750000,  1, 40, 8, 1},
2618e2bab3fSAlgea Cao 	{33750000,  1, 80, 8, 2},
2628e2bab3fSAlgea Cao 	{33750000,  1, 10, 2, 4},
2638e2bab3fSAlgea Cao 	{74250000,  1, 40, 8, 1},
2648e2bab3fSAlgea Cao 	{74250000, 18, 80, 8, 2},
265cb24dc0eSAlgea Cao 	{74250000,  1, 20, 4, 8},
2668e2bab3fSAlgea Cao 	{148500000, 2, 40, 4, 3},
267cb24dc0eSAlgea Cao 	{148500000, 1, 10, 2, 8},
2688e2bab3fSAlgea Cao 	{297000000, 4, 40, 2, 3},
269cb24dc0eSAlgea Cao 	{297000000, 2, 20, 2, 8},
2708e2bab3fSAlgea Cao 	{594000000, 8, 40, 1, 3},
271cb24dc0eSAlgea Cao 	{594000000, 4, 20, 1, 8},
2728e2bab3fSAlgea Cao 	{     ~0UL, 0,  0, 0, 0}
2738e2bab3fSAlgea Cao };
2748e2bab3fSAlgea Cao 
2758e2bab3fSAlgea Cao static const struct phy_config rk3228_phy_cfg[] = {
2768e2bab3fSAlgea Cao 	{	165000000, {
2778e2bab3fSAlgea Cao 			0xaa, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
2788e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
2798e2bab3fSAlgea Cao 		},
2808e2bab3fSAlgea Cao 	}, {
2818e2bab3fSAlgea Cao 		340000000, {
2828e2bab3fSAlgea Cao 			0xaa, 0x15, 0x6a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
2838e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
2848e2bab3fSAlgea Cao 		},
2858e2bab3fSAlgea Cao 	}, {
2868e2bab3fSAlgea Cao 		594000000, {
2878e2bab3fSAlgea Cao 			0xaa, 0x15, 0x7a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
2888e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
2898e2bab3fSAlgea Cao 		},
2908e2bab3fSAlgea Cao 	}, {
2918e2bab3fSAlgea Cao 		~0UL, {
2928e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2938e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
2948e2bab3fSAlgea Cao 		},
2958e2bab3fSAlgea Cao 	}
2968e2bab3fSAlgea Cao };
2978e2bab3fSAlgea Cao 
2988e2bab3fSAlgea Cao static const struct phy_config rk3328_phy_cfg[] = {
2998e2bab3fSAlgea Cao 	{	165000000, {
3008e2bab3fSAlgea Cao 			0x07, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08,
3018e2bab3fSAlgea Cao 			0x00, 0xac, 0xcc, 0xcc, 0xcc,
3028e2bab3fSAlgea Cao 		},
3038e2bab3fSAlgea Cao 	}, {
3048e2bab3fSAlgea Cao 		340000000, {
3058e2bab3fSAlgea Cao 			0x0b, 0x0d, 0x0d, 0x0d, 0x07, 0x15, 0x08, 0x08, 0x08,
3068e2bab3fSAlgea Cao 			0x3f, 0xac, 0xcc, 0xcd, 0xdd,
3078e2bab3fSAlgea Cao 		},
3088e2bab3fSAlgea Cao 	}, {
3098e2bab3fSAlgea Cao 		594000000, {
3108e2bab3fSAlgea Cao 			0x10, 0x1a, 0x1a, 0x1a, 0x07, 0x15, 0x08, 0x08, 0x08,
3118e2bab3fSAlgea Cao 			0x00, 0xac, 0xcc, 0xcc, 0xcc,
3128e2bab3fSAlgea Cao 		},
3138e2bab3fSAlgea Cao 	}, {
3148e2bab3fSAlgea Cao 		~0UL, {
3158e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3168e2bab3fSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
3178e2bab3fSAlgea Cao 		},
3188e2bab3fSAlgea Cao 	}
3198e2bab3fSAlgea Cao };
3208e2bab3fSAlgea Cao 
32119957ff8SAlgea Cao static const struct phy_config rk3528_phy_cfg[] = {
32219957ff8SAlgea Cao 	/* tmdsclk bias-clk bias-data voltage-clk voltage-data pre-emphasis-data */
32319957ff8SAlgea Cao 	{	165000000, {
324*27c6606cSAlgea Cao 			0x02, 0x04, 0x0f, 0x0f, 0x00, 0x76, 0x83, 0x0a, 0x0a,
325*27c6606cSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
326*27c6606cSAlgea Cao 		},
327*27c6606cSAlgea Cao 	}, {
328*27c6606cSAlgea Cao 		185625000, {
329*27c6606cSAlgea Cao 			0x02, 0x0b, 0x0f, 0x0f, 0x00, 0x76, 0x83, 0x0a, 0x33,
33019957ff8SAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
33119957ff8SAlgea Cao 		},
33219957ff8SAlgea Cao 	}, {
33319957ff8SAlgea Cao 		340000000, {
334*27c6606cSAlgea Cao 			0x03, 0x04, 0x0c, 0x12, 0x00, 0x76, 0x83, 0x00, 0x0f,
335*27c6606cSAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
336*27c6606cSAlgea Cao 		},
337*27c6606cSAlgea Cao 	}, {
338*27c6606cSAlgea Cao 		371250000, {
339*27c6606cSAlgea Cao 			0x02, 0x0b, 0x0d, 0x18, 0x00, 0x76, 0x83, 0x0a, 0x33,
34019957ff8SAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
34119957ff8SAlgea Cao 		},
34219957ff8SAlgea Cao 	}, {
34319957ff8SAlgea Cao 		594000000, {
344*27c6606cSAlgea Cao 			0x02, 0x0b, 0x0d, 0x18, 0x00, 0x76, 0x83, 0x0a, 0x33,
34519957ff8SAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
34619957ff8SAlgea Cao 		},
34719957ff8SAlgea Cao 	}, {
34819957ff8SAlgea Cao 		~0UL, {
34919957ff8SAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35019957ff8SAlgea Cao 			0x00, 0x00, 0x00, 0x00, 0x00,
35119957ff8SAlgea Cao 		},
35219957ff8SAlgea Cao 	}
35319957ff8SAlgea Cao };
35419957ff8SAlgea Cao 
inno_write(struct inno_hdmi_phy * inno,u32 reg,u8 val)3558e2bab3fSAlgea Cao static inline void inno_write(struct inno_hdmi_phy *inno, u32 reg, u8 val)
3568e2bab3fSAlgea Cao {
3578e2bab3fSAlgea Cao 	writel(val, inno->regs + (reg * 4));
3588e2bab3fSAlgea Cao }
3598e2bab3fSAlgea Cao 
inno_read(struct inno_hdmi_phy * inno,u32 reg)3608e2bab3fSAlgea Cao static inline u8 inno_read(struct inno_hdmi_phy *inno, u32 reg)
3618e2bab3fSAlgea Cao {
3628e2bab3fSAlgea Cao 	u32 val;
3638e2bab3fSAlgea Cao 
3648e2bab3fSAlgea Cao 	val = readl(inno->regs + (reg * 4));
3658e2bab3fSAlgea Cao 
3668e2bab3fSAlgea Cao 	return val;
3678e2bab3fSAlgea Cao }
3688e2bab3fSAlgea Cao 
inno_update_bits(struct inno_hdmi_phy * inno,u8 reg,u8 mask,u8 val)3698e2bab3fSAlgea Cao static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg,
3708e2bab3fSAlgea Cao 				    u8 mask, u8 val)
3718e2bab3fSAlgea Cao {
3728e2bab3fSAlgea Cao 	u32 tmp, orig;
3738e2bab3fSAlgea Cao 
3748e2bab3fSAlgea Cao 	orig = inno_read(inno, reg);
3758e2bab3fSAlgea Cao 	tmp = orig & ~mask;
3768e2bab3fSAlgea Cao 	tmp |= val & mask;
3778e2bab3fSAlgea Cao 	inno_write(inno, reg, tmp);
3788e2bab3fSAlgea Cao }
3798e2bab3fSAlgea Cao 
inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy * inno,unsigned long rate)3808e2bab3fSAlgea Cao static u32 inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
3818e2bab3fSAlgea Cao 				     unsigned long rate)
3828e2bab3fSAlgea Cao {
3838e2bab3fSAlgea Cao 	u32 tmdsclk;
3848e2bab3fSAlgea Cao 
3858e2bab3fSAlgea Cao 	switch (inno->bus_width) {
3868e2bab3fSAlgea Cao 	case 4:
3878e2bab3fSAlgea Cao 		tmdsclk = (u32)rate / 2;
3888e2bab3fSAlgea Cao 		break;
3898e2bab3fSAlgea Cao 	case 5:
3908e2bab3fSAlgea Cao 		tmdsclk = (u32)rate * 5 / 8;
3918e2bab3fSAlgea Cao 		break;
3928e2bab3fSAlgea Cao 	case 6:
3938e2bab3fSAlgea Cao 		tmdsclk = (u32)rate * 3 / 4;
3948e2bab3fSAlgea Cao 		break;
3958e2bab3fSAlgea Cao 	case 10:
3968e2bab3fSAlgea Cao 		tmdsclk = (u32)rate * 5 / 4;
3978e2bab3fSAlgea Cao 		break;
3988e2bab3fSAlgea Cao 	case 12:
3998e2bab3fSAlgea Cao 		tmdsclk = (u32)rate * 3 / 2;
4008e2bab3fSAlgea Cao 		break;
4018e2bab3fSAlgea Cao 	case 16:
4028e2bab3fSAlgea Cao 		tmdsclk = (u32)rate * 2;
4038e2bab3fSAlgea Cao 		break;
4048e2bab3fSAlgea Cao 	default:
4058e2bab3fSAlgea Cao 		tmdsclk = rate;
4068e2bab3fSAlgea Cao 	}
4078e2bab3fSAlgea Cao 
4088e2bab3fSAlgea Cao 	return tmdsclk;
4098e2bab3fSAlgea Cao }
4108e2bab3fSAlgea Cao 
rk_get_cpu_version(void)4118e2bab3fSAlgea Cao static u8 rk_get_cpu_version(void)
4128e2bab3fSAlgea Cao {
4138e2bab3fSAlgea Cao 	u8 val = 0;
4148e2bab3fSAlgea Cao #ifdef CONFIG_ROCKCHIP_EFUSE
4158e2bab3fSAlgea Cao 	struct udevice *dev;
4168e2bab3fSAlgea Cao 	u32 regs[2] = {0};
4178e2bab3fSAlgea Cao 	u8 fuses[1];
4188e2bab3fSAlgea Cao 	ofnode node;
4198e2bab3fSAlgea Cao 	int ret;
4208e2bab3fSAlgea Cao 
4218e2bab3fSAlgea Cao 	ret = uclass_get_device_by_driver(UCLASS_MISC,
4228e2bab3fSAlgea Cao 					  DM_GET_DRIVER(rockchip_efuse), &dev);
4238e2bab3fSAlgea Cao 	if (ret) {
4248e2bab3fSAlgea Cao 		printf("%s: no misc-device found\n", __func__);
4258e2bab3fSAlgea Cao 		return -EINVAL;
4268e2bab3fSAlgea Cao 	}
4278e2bab3fSAlgea Cao 
4288e2bab3fSAlgea Cao 	node = dev_read_subnode(dev, "cpu-version");
4298e2bab3fSAlgea Cao 	if (!ofnode_valid(node))
4308e2bab3fSAlgea Cao 		return -EINVAL;
4318e2bab3fSAlgea Cao 
4328e2bab3fSAlgea Cao 	ret = ofnode_read_u32_array(node, "reg", regs, 2);
4338e2bab3fSAlgea Cao 	if (ret) {
4348e2bab3fSAlgea Cao 		printf("Cannot get efuse reg\n");
4358e2bab3fSAlgea Cao 		return -EINVAL;
4368e2bab3fSAlgea Cao 	}
4378e2bab3fSAlgea Cao 
4388e2bab3fSAlgea Cao 	ret = misc_read(dev, regs[0], &fuses, regs[1]);
4398e2bab3fSAlgea Cao 	if (ret) {
4408e2bab3fSAlgea Cao 		printf("%s: misc_read failed\n", __func__);
4418e2bab3fSAlgea Cao 		return 0;
4428e2bab3fSAlgea Cao 	}
4438e2bab3fSAlgea Cao 
4448e2bab3fSAlgea Cao 	val = fuses[0];
4458e2bab3fSAlgea Cao 	val = (val >> 3) & 0x1;
4468e2bab3fSAlgea Cao #endif
4478e2bab3fSAlgea Cao 	return val;
4488e2bab3fSAlgea Cao }
4498e2bab3fSAlgea Cao 
inno_hdmi_phy_power_on(struct rockchip_phy * phy)45015081c50SWyon Bi static int inno_hdmi_phy_power_on(struct rockchip_phy *phy)
4518e2bab3fSAlgea Cao {
4526a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
4536a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
4546a3f4548SSandy Huang #else
45515081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
4566a3f4548SSandy Huang #endif
4578e2bab3fSAlgea Cao 	const struct post_pll_config *cfg = post_pll_cfg_table;
4588e2bab3fSAlgea Cao 	const struct phy_config *phy_cfg = inno->plat_data->phy_cfg_table;
4598e2bab3fSAlgea Cao 	u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock);
4608e2bab3fSAlgea Cao 	u32 chipversion = 1;
4618e2bab3fSAlgea Cao 
4628e2bab3fSAlgea Cao 	printf("start Inno HDMI PHY Power On\n");
4638e2bab3fSAlgea Cao 
4648e2bab3fSAlgea Cao 	if (inno->phy_cfg)
4658e2bab3fSAlgea Cao 		phy_cfg = inno->phy_cfg;
4668e2bab3fSAlgea Cao 
4678e2bab3fSAlgea Cao 	if (!tmdsclock) {
4688e2bab3fSAlgea Cao 		printf("TMDS clock is zero!\n");
4698e2bab3fSAlgea Cao 		return -EINVAL;
4708e2bab3fSAlgea Cao 	}
4718e2bab3fSAlgea Cao 
4728e2bab3fSAlgea Cao 	if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3328 &&
4738e2bab3fSAlgea Cao 	    rk_get_cpu_version())
4748e2bab3fSAlgea Cao 		chipversion = 2;
4758e2bab3fSAlgea Cao 	else if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228 &&
4768e2bab3fSAlgea Cao 		 tmdsclock <= 33750000)
4778e2bab3fSAlgea Cao 		chipversion = 4;
47819957ff8SAlgea Cao 	else if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3528)
479cb24dc0eSAlgea Cao 		chipversion = 8;
4808e2bab3fSAlgea Cao 
4818e2bab3fSAlgea Cao 	printf("tmdsclock = %d; chipversion = %d\n", tmdsclock, chipversion);
4828e2bab3fSAlgea Cao 
4838e2bab3fSAlgea Cao 	for (; cfg->tmdsclock != ~0UL; cfg++)
4848e2bab3fSAlgea Cao 		if (tmdsclock <= cfg->tmdsclock &&
4858e2bab3fSAlgea Cao 		    cfg->version & chipversion)
4868e2bab3fSAlgea Cao 			break;
4878e2bab3fSAlgea Cao 
4888e2bab3fSAlgea Cao 	for (; phy_cfg->tmdsclock != ~0UL; phy_cfg++)
4898e2bab3fSAlgea Cao 		if (tmdsclock <= phy_cfg->tmdsclock)
4908e2bab3fSAlgea Cao 			break;
4918e2bab3fSAlgea Cao 
4928e2bab3fSAlgea Cao 	if (cfg->tmdsclock == ~0UL || phy_cfg->tmdsclock == ~0UL)
4938e2bab3fSAlgea Cao 		return -EINVAL;
4948e2bab3fSAlgea Cao 
4958e2bab3fSAlgea Cao 	printf("Inno HDMI PHY Power On\n");
4968e2bab3fSAlgea Cao 	if (inno->plat_data->ops->power_on)
4978e2bab3fSAlgea Cao 		return inno->plat_data->ops->power_on(inno, cfg, phy_cfg);
4988e2bab3fSAlgea Cao 	else
4998e2bab3fSAlgea Cao 		return -EINVAL;
5008e2bab3fSAlgea Cao }
5018e2bab3fSAlgea Cao 
inno_hdmi_phy_power_off(struct rockchip_phy * phy)50215081c50SWyon Bi static int inno_hdmi_phy_power_off(struct rockchip_phy *phy)
5038e2bab3fSAlgea Cao {
5046a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
5056a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
5066a3f4548SSandy Huang #else
50715081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
5086a3f4548SSandy Huang #endif
5098e2bab3fSAlgea Cao 
5108e2bab3fSAlgea Cao 	if (inno->plat_data->ops->power_off)
5118e2bab3fSAlgea Cao 		inno->plat_data->ops->power_off(inno);
5128e2bab3fSAlgea Cao 	printf("Inno HDMI PHY Power Off\n");
5138e2bab3fSAlgea Cao 
5148e2bab3fSAlgea Cao 	return 0;
5158e2bab3fSAlgea Cao }
5168e2bab3fSAlgea Cao 
inno_hdmi_phy_clk_is_prepared(struct inno_hdmi_phy * inno)51715081c50SWyon Bi static int inno_hdmi_phy_clk_is_prepared(struct inno_hdmi_phy *inno)
5188e2bab3fSAlgea Cao {
5198e2bab3fSAlgea Cao 	u8 status;
5208e2bab3fSAlgea Cao 
5218e2bab3fSAlgea Cao 	if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228)
5228e2bab3fSAlgea Cao 		status = inno_read(inno, 0xe0) & PRE_PLL_POWER_MASK;
5238e2bab3fSAlgea Cao 	else
5248e2bab3fSAlgea Cao 		status = inno_read(inno, 0xa0) & 1;
5258e2bab3fSAlgea Cao 
5268e2bab3fSAlgea Cao 	return status ? 0 : 1;
5278e2bab3fSAlgea Cao }
5288e2bab3fSAlgea Cao 
inno_hdmi_phy_clk_prepare(struct inno_hdmi_phy * inno)52915081c50SWyon Bi static int inno_hdmi_phy_clk_prepare(struct inno_hdmi_phy *inno)
5308e2bab3fSAlgea Cao {
5318e2bab3fSAlgea Cao 	if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228)
5328e2bab3fSAlgea Cao 		inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK,
5338e2bab3fSAlgea Cao 				 PRE_PLL_POWER_UP);
5348e2bab3fSAlgea Cao 	else
5358e2bab3fSAlgea Cao 		inno_update_bits(inno, 0xa0, 1, 0);
5368e2bab3fSAlgea Cao 
5378e2bab3fSAlgea Cao 	return 0;
5388e2bab3fSAlgea Cao }
5398e2bab3fSAlgea Cao 
inno_hdmi_phy_clk_set_rate(struct inno_hdmi_phy * inno,unsigned long rate)54015081c50SWyon Bi static int inno_hdmi_phy_clk_set_rate(struct inno_hdmi_phy *inno,
5418e2bab3fSAlgea Cao 				      unsigned long rate)
5428e2bab3fSAlgea Cao {
5438e2bab3fSAlgea Cao 	const struct pre_pll_config *cfg = pre_pll_cfg_table;
5448e2bab3fSAlgea Cao 	u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
5458e2bab3fSAlgea Cao 
5468e2bab3fSAlgea Cao 	for (; cfg->pixclock != ~0UL; cfg++)
5478e2bab3fSAlgea Cao 		if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock)
5488e2bab3fSAlgea Cao 			break;
5498e2bab3fSAlgea Cao 
5508e2bab3fSAlgea Cao 	if (cfg->pixclock == ~0UL) {
5518e2bab3fSAlgea Cao 		printf("unsupported rate %lu\n", rate);
5528e2bab3fSAlgea Cao 		return -EINVAL;
5538e2bab3fSAlgea Cao 	}
5548e2bab3fSAlgea Cao 
5558e2bab3fSAlgea Cao 	if (inno->plat_data->ops->pre_pll_update)
5568e2bab3fSAlgea Cao 		inno->plat_data->ops->pre_pll_update(inno, cfg);
5578e2bab3fSAlgea Cao 
5588e2bab3fSAlgea Cao 	inno->pixclock = rate;
5598e2bab3fSAlgea Cao 
5608e2bab3fSAlgea Cao 	return 0;
5618e2bab3fSAlgea Cao }
5628e2bab3fSAlgea Cao 
inno_hdmi_phy_rk3228_init(struct inno_hdmi_phy * inno)5638e2bab3fSAlgea Cao static void inno_hdmi_phy_rk3228_init(struct inno_hdmi_phy *inno)
5648e2bab3fSAlgea Cao {
5658e2bab3fSAlgea Cao 	u32 m, v;
5668e2bab3fSAlgea Cao 
5678e2bab3fSAlgea Cao 	/*
5688e2bab3fSAlgea Cao 	 * Use phy internal register control
5698e2bab3fSAlgea Cao 	 * rxsense/poweron/pllpd/pdataen signal.
5708e2bab3fSAlgea Cao 	 */
5718e2bab3fSAlgea Cao 	m = BYPASS_RXSENSE_EN_MASK | BYPASS_PWRON_EN_MASK |
5728e2bab3fSAlgea Cao 	    BYPASS_PLLPD_EN_MASK;
5738e2bab3fSAlgea Cao 	v = BYPASS_RXSENSE_EN | BYPASS_PWRON_EN | BYPASS_PLLPD_EN;
5748e2bab3fSAlgea Cao 	inno_update_bits(inno, 0x01, m, v);
5758e2bab3fSAlgea Cao 	inno_update_bits(inno, 0x02, BYPASS_PDATA_EN_MASK, BYPASS_PDATA_EN);
5768e2bab3fSAlgea Cao 
5778e2bab3fSAlgea Cao 	/* manual power down post-PLL */
5788e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xaa, POST_PLL_CTRL_MASK, POST_PLL_CTRL_MANUAL);
5798e2bab3fSAlgea Cao }
5808e2bab3fSAlgea Cao 
5818e2bab3fSAlgea Cao static int
inno_hdmi_phy_rk3228_power_on(struct inno_hdmi_phy * inno,const struct post_pll_config * cfg,const struct phy_config * phy_cfg)5828e2bab3fSAlgea Cao inno_hdmi_phy_rk3228_power_on(struct inno_hdmi_phy *inno,
5838e2bab3fSAlgea Cao 			      const struct post_pll_config *cfg,
5848e2bab3fSAlgea Cao 			      const struct phy_config *phy_cfg)
5858e2bab3fSAlgea Cao {
5868e2bab3fSAlgea Cao 	int pll_tries;
5878e2bab3fSAlgea Cao 	u32 m, v;
5888e2bab3fSAlgea Cao 
5898e2bab3fSAlgea Cao 	/* pdata_en disable */
5908e2bab3fSAlgea Cao 	inno_update_bits(inno, 0x02, PDATAEN_MASK, PDATAEN_DISABLE);
5918e2bab3fSAlgea Cao 
5928e2bab3fSAlgea Cao 	/* Power down Post-PLL */
5938e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_DOWN);
5948e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, POST_PLL_POWER_MASK, POST_PLL_POWER_DOWN);
5958e2bab3fSAlgea Cao 
5968e2bab3fSAlgea Cao 	/* Post-PLL update */
5978e2bab3fSAlgea Cao 	m = POST_PLL_PRE_DIV_MASK;
5988e2bab3fSAlgea Cao 	v = POST_PLL_PRE_DIV(cfg->prediv);
5998e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe9, m, v);
6008e2bab3fSAlgea Cao 
6018e2bab3fSAlgea Cao 	m = POST_PLL_FB_DIV_8_MASK;
6028e2bab3fSAlgea Cao 	v = POST_PLL_FB_DIV_8(cfg->fbdiv >> 8);
6038e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xeb, m, v);
6048e2bab3fSAlgea Cao 	inno_write(inno, 0xea, POST_PLL_FB_DIV_7_0(cfg->fbdiv));
6058e2bab3fSAlgea Cao 
6068e2bab3fSAlgea Cao 	if (cfg->postdiv == 1) {
6078e2bab3fSAlgea Cao 		/* Disable Post-PLL post divider */
6088e2bab3fSAlgea Cao 		m = POST_PLL_POST_DIV_EN_MASK;
6098e2bab3fSAlgea Cao 		v = POST_PLL_POST_DIV_DISABLE;
6108e2bab3fSAlgea Cao 		inno_update_bits(inno, 0xe9, m, v);
6118e2bab3fSAlgea Cao 	} else {
6128e2bab3fSAlgea Cao 		/* Enable Post-PLL post divider */
6138e2bab3fSAlgea Cao 		m = POST_PLL_POST_DIV_EN_MASK;
6148e2bab3fSAlgea Cao 		v = POST_PLL_POST_DIV_ENABLE;
6158e2bab3fSAlgea Cao 		inno_update_bits(inno, 0xe9, m, v);
6168e2bab3fSAlgea Cao 
6178e2bab3fSAlgea Cao 		m = POST_PLL_POST_DIV_MASK;
6188e2bab3fSAlgea Cao 		v = POST_PLL_POST_DIV(cfg->postdiv / 2 - 1);
6198e2bab3fSAlgea Cao 		inno_update_bits(inno, 0xeb, m, v);
6208e2bab3fSAlgea Cao 	}
6218e2bab3fSAlgea Cao 
6228e2bab3fSAlgea Cao 	for (v = 0; v < 4; v++)
6238e2bab3fSAlgea Cao 		inno_write(inno, 0xef + v, phy_cfg->regs[v]);
6248e2bab3fSAlgea Cao 
6258e2bab3fSAlgea Cao 	/* Power up Post-PLL */
6268e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, POST_PLL_POWER_MASK, POST_PLL_POWER_UP);
6278e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_UP);
6288e2bab3fSAlgea Cao 
6298e2bab3fSAlgea Cao 	/* BandGap enable */
6308e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe1, BANDGAP_MASK, BANDGAP_ENABLE);
6318e2bab3fSAlgea Cao 
6328e2bab3fSAlgea Cao 	/* TMDS driver enable */
6338e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe1, TMDS_DRIVER_MASK, TMDS_DRIVER_ENABLE);
6348e2bab3fSAlgea Cao 
6358e2bab3fSAlgea Cao 	/* Wait for post PLL lock */
6368e2bab3fSAlgea Cao 	pll_tries = 0;
6378e2bab3fSAlgea Cao 	while (!(inno_read(inno, 0xeb) & POST_PLL_LOCK_STATUS)) {
6388e2bab3fSAlgea Cao 		if (pll_tries == INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT) {
6398e2bab3fSAlgea Cao 			printf("Post-PLL unlock\n");
6408e2bab3fSAlgea Cao 			return -ETIMEDOUT;
6418e2bab3fSAlgea Cao 		}
6428e2bab3fSAlgea Cao 
6438e2bab3fSAlgea Cao 		pll_tries++;
6448e2bab3fSAlgea Cao 		udelay(100);
6458e2bab3fSAlgea Cao 	}
6468e2bab3fSAlgea Cao 
6478e2bab3fSAlgea Cao 	if (cfg->tmdsclock > 340000000)
6488e2bab3fSAlgea Cao 		mdelay(100);
6498e2bab3fSAlgea Cao 
6508e2bab3fSAlgea Cao 	/* pdata_en enable */
6518e2bab3fSAlgea Cao 	inno_update_bits(inno, 0x02, PDATAEN_MASK, PDATAEN_ENABLE);
6528e2bab3fSAlgea Cao 	return 0;
6538e2bab3fSAlgea Cao }
6548e2bab3fSAlgea Cao 
inno_hdmi_phy_rk3228_power_off(struct inno_hdmi_phy * inno)6558e2bab3fSAlgea Cao static void inno_hdmi_phy_rk3228_power_off(struct inno_hdmi_phy *inno)
6568e2bab3fSAlgea Cao {
6578e2bab3fSAlgea Cao 	/* TMDS driver Disable */
6588e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe1, TMDS_DRIVER_MASK, TMDS_DRIVER_DISABLE);
6598e2bab3fSAlgea Cao 
6608e2bab3fSAlgea Cao 	/* BandGap Disable */
6618e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe1, BANDGAP_MASK, BANDGAP_DISABLE);
6628e2bab3fSAlgea Cao 
6638e2bab3fSAlgea Cao 	/* Post-PLL power down */
6648e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, POST_PLL_POWER_MASK, POST_PLL_POWER_DOWN);
6658e2bab3fSAlgea Cao }
6668e2bab3fSAlgea Cao 
6678e2bab3fSAlgea Cao static int
inno_hdmi_phy_rk3228_pre_pll_update(struct inno_hdmi_phy * inno,const struct pre_pll_config * cfg)6688e2bab3fSAlgea Cao inno_hdmi_phy_rk3228_pre_pll_update(struct inno_hdmi_phy *inno,
6698e2bab3fSAlgea Cao 				    const struct pre_pll_config *cfg)
6708e2bab3fSAlgea Cao {
6718e2bab3fSAlgea Cao 	int pll_tries;
6728e2bab3fSAlgea Cao 	u32 m, v;
6738e2bab3fSAlgea Cao 
6748e2bab3fSAlgea Cao 	/* Power down PRE-PLL */
6758e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_DOWN);
6768e2bab3fSAlgea Cao 
6778e2bab3fSAlgea Cao 	m = PRE_PLL_FB_DIV_8_MASK | PCLK_VCO_DIV_5_MASK | PRE_PLL_PRE_DIV_MASK;
6788e2bab3fSAlgea Cao 	v = PRE_PLL_FB_DIV_8(cfg->fbdiv >> 8) |
6798e2bab3fSAlgea Cao 	    PCLK_VCO_DIV_5(cfg->vco_div_5_en) | PRE_PLL_PRE_DIV(cfg->prediv);
6808e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe2, m, v);
6818e2bab3fSAlgea Cao 
6828e2bab3fSAlgea Cao 	inno_write(inno, 0xe3, PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
6838e2bab3fSAlgea Cao 
6848e2bab3fSAlgea Cao 	m = PRE_PLL_PCLK_DIV_B_MASK | PRE_PLL_PCLK_DIV_A_MASK;
6858e2bab3fSAlgea Cao 	v = PRE_PLL_PCLK_DIV_B(cfg->pclk_div_b) |
6868e2bab3fSAlgea Cao 	    PRE_PLL_PCLK_DIV_A(cfg->pclk_div_a);
6878e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe4, m, v);
6888e2bab3fSAlgea Cao 
6898e2bab3fSAlgea Cao 	m = PRE_PLL_PCLK_DIV_C_MASK | PRE_PLL_PCLK_DIV_D_MASK;
6908e2bab3fSAlgea Cao 	v = PRE_PLL_PCLK_DIV_C(cfg->pclk_div_c) |
6918e2bab3fSAlgea Cao 	    PRE_PLL_PCLK_DIV_D(cfg->pclk_div_d);
6928e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe5, m, v);
6938e2bab3fSAlgea Cao 
6948e2bab3fSAlgea Cao 	m = PRE_PLL_TMDSCLK_DIV_C_MASK | PRE_PLL_TMDSCLK_DIV_A_MASK |
6958e2bab3fSAlgea Cao 	    PRE_PLL_TMDSCLK_DIV_B_MASK;
6968e2bab3fSAlgea Cao 	v = PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c) |
6978e2bab3fSAlgea Cao 	    PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
6988e2bab3fSAlgea Cao 	    PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b);
6998e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe6, m, v);
7008e2bab3fSAlgea Cao 
7018e2bab3fSAlgea Cao 	/* Power up PRE-PLL */
7028e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_UP);
7038e2bab3fSAlgea Cao 
7048e2bab3fSAlgea Cao 	/* Wait for Pre-PLL lock */
7058e2bab3fSAlgea Cao 	pll_tries = 0;
7068e2bab3fSAlgea Cao 	while (!(inno_read(inno, 0xe8) & PRE_PLL_LOCK_STATUS)) {
7078e2bab3fSAlgea Cao 		if (pll_tries == INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT) {
7088e2bab3fSAlgea Cao 			printf("Pre-PLL unlock\n");
7098e2bab3fSAlgea Cao 			return -ETIMEDOUT;
7108e2bab3fSAlgea Cao 		}
7118e2bab3fSAlgea Cao 
7128e2bab3fSAlgea Cao 		pll_tries++;
7138e2bab3fSAlgea Cao 		udelay(100);
7148e2bab3fSAlgea Cao 	}
7158e2bab3fSAlgea Cao 
7168e2bab3fSAlgea Cao 	return 0;
7178e2bab3fSAlgea Cao }
7188e2bab3fSAlgea Cao 
inno_hdmi_phy_rk3328_init(struct inno_hdmi_phy * inno)7198e2bab3fSAlgea Cao static void inno_hdmi_phy_rk3328_init(struct inno_hdmi_phy *inno)
7208e2bab3fSAlgea Cao {
7218e2bab3fSAlgea Cao 	/*
7228e2bab3fSAlgea Cao 	 * Use phy internal register control
7238e2bab3fSAlgea Cao 	 * rxsense/poweron/pllpd/pdataen signal.
7248e2bab3fSAlgea Cao 	 */
7258e2bab3fSAlgea Cao 	inno_write(inno, 0x01, 0x07);
7268e2bab3fSAlgea Cao 	inno_write(inno, 0x02, 0x91);
7278e2bab3fSAlgea Cao }
7288e2bab3fSAlgea Cao 
7298e2bab3fSAlgea Cao static int
inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy * inno,const struct post_pll_config * cfg,const struct phy_config * phy_cfg)7308e2bab3fSAlgea Cao inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
7318e2bab3fSAlgea Cao 			      const struct post_pll_config *cfg,
7328e2bab3fSAlgea Cao 			      const struct phy_config *phy_cfg)
7338e2bab3fSAlgea Cao {
7348e2bab3fSAlgea Cao 	u32 val;
7358e2bab3fSAlgea Cao 
7368e2bab3fSAlgea Cao 	/* set pdata_en to 0 */
7378e2bab3fSAlgea Cao 	inno_update_bits(inno, 0x02, 1, 0);
7388e2bab3fSAlgea Cao 	/* Power off post PLL */
7398e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xaa, 1, 1);
7408e2bab3fSAlgea Cao 
7418e2bab3fSAlgea Cao 	val = cfg->fbdiv & 0xff;
7428e2bab3fSAlgea Cao 	inno_write(inno, 0xac, val);
7438e2bab3fSAlgea Cao 	if (cfg->postdiv == 1) {
7448e2bab3fSAlgea Cao 		inno_write(inno, 0xaa, 2);
7458e2bab3fSAlgea Cao 		val = (cfg->fbdiv >> 8) | cfg->prediv;
7468e2bab3fSAlgea Cao 		inno_write(inno, 0xab, val);
7478e2bab3fSAlgea Cao 	} else {
7488e2bab3fSAlgea Cao 		val = (cfg->postdiv / 2) - 1;
7498e2bab3fSAlgea Cao 		inno_write(inno, 0xad, val);
7508e2bab3fSAlgea Cao 		val = (cfg->fbdiv >> 8) | cfg->prediv;
7518e2bab3fSAlgea Cao 		inno_write(inno, 0xab, val);
7528e2bab3fSAlgea Cao 		inno_write(inno, 0xaa, 0x0e);
7538e2bab3fSAlgea Cao 	}
7548e2bab3fSAlgea Cao 
7558e2bab3fSAlgea Cao 	for (val = 0; val < 14; val++)
7568e2bab3fSAlgea Cao 		inno_write(inno, 0xb5 + val, phy_cfg->regs[val]);
7578e2bab3fSAlgea Cao 
7588e2bab3fSAlgea Cao 	/* bit[7:6] of reg c8/c9/ca/c8 is ESD detect threshold:
7598e2bab3fSAlgea Cao 	 * 00 - 340mV
7608e2bab3fSAlgea Cao 	 * 01 - 280mV
7618e2bab3fSAlgea Cao 	 * 10 - 260mV
7628e2bab3fSAlgea Cao 	 * 11 - 240mV
7638e2bab3fSAlgea Cao 	 * default is 240mV, now we set it to 340mV
7648e2bab3fSAlgea Cao 	 */
7658e2bab3fSAlgea Cao 	inno_write(inno, 0xc8, 0);
7668e2bab3fSAlgea Cao 	inno_write(inno, 0xc9, 0);
7678e2bab3fSAlgea Cao 	inno_write(inno, 0xca, 0);
7688e2bab3fSAlgea Cao 	inno_write(inno, 0xcb, 0);
7698e2bab3fSAlgea Cao 
7708e2bab3fSAlgea Cao 	if (phy_cfg->tmdsclock > 340000000) {
7718e2bab3fSAlgea Cao 		/* Set termination resistor to 100ohm */
7728e2bab3fSAlgea Cao 		val = 75000000 / 100000;
7738e2bab3fSAlgea Cao 		inno_write(inno, 0xc5, ((val >> 8) & 0xff) | 0x80);
7748e2bab3fSAlgea Cao 		inno_write(inno, 0xc6, val & 0xff);
7758e2bab3fSAlgea Cao 		inno_write(inno, 0xc7, 3 << 1);
7768e2bab3fSAlgea Cao 		inno_write(inno, 0xc5, ((val >> 8) & 0xff));
7778e2bab3fSAlgea Cao 	} else if (phy_cfg->tmdsclock > 165000000) {
7788e2bab3fSAlgea Cao 		inno_write(inno, 0xc5, 0x81);
7798e2bab3fSAlgea Cao 		/* clk termination resistor is 50ohm
7808e2bab3fSAlgea Cao 		 * data termination resistor is 150ohm
7818e2bab3fSAlgea Cao 		 */
7828e2bab3fSAlgea Cao 		inno_write(inno, 0xc8, 0x30);
7838e2bab3fSAlgea Cao 		inno_write(inno, 0xc9, 0x10);
7848e2bab3fSAlgea Cao 		inno_write(inno, 0xca, 0x10);
7858e2bab3fSAlgea Cao 		inno_write(inno, 0xcb, 0x10);
7868e2bab3fSAlgea Cao 	} else {
7878e2bab3fSAlgea Cao 		inno_write(inno, 0xc5, 0x81);
7888e2bab3fSAlgea Cao 	}
7898e2bab3fSAlgea Cao 
7908e2bab3fSAlgea Cao 	/* set TMDS sync detection counter length */
7918e2bab3fSAlgea Cao 	val = 47520000000UL / phy_cfg->tmdsclock;
7928e2bab3fSAlgea Cao 	inno_write(inno, 0xd8, (val >> 8) & 0xff);
7938e2bab3fSAlgea Cao 	inno_write(inno, 0xd9, val & 0xff);
7948e2bab3fSAlgea Cao 
7958e2bab3fSAlgea Cao 	/* Power up post PLL */
7968e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xaa, 1, 0);
7978e2bab3fSAlgea Cao 	/* Power up tmds driver */
7988e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xb0, 4, 4);
7998e2bab3fSAlgea Cao 	inno_write(inno, 0xb2, 0x0f);
8008e2bab3fSAlgea Cao 
8018e2bab3fSAlgea Cao 	/* Wait for post PLL lock */
8028e2bab3fSAlgea Cao 	for (val = 0; val < 5; val++) {
8038e2bab3fSAlgea Cao 		if (inno_read(inno, 0xaf) & 1)
8048e2bab3fSAlgea Cao 			break;
8058e2bab3fSAlgea Cao 		udelay(1000);
8068e2bab3fSAlgea Cao 	}
8078e2bab3fSAlgea Cao 	if (!(inno_read(inno, 0xaf) & 1)) {
8088e2bab3fSAlgea Cao 		printf("HDMI PHY Post PLL unlock\n");
8098e2bab3fSAlgea Cao 		return -ETIMEDOUT;
8108e2bab3fSAlgea Cao 	}
8118e2bab3fSAlgea Cao 	if (phy_cfg->tmdsclock > 340000000)
8128e2bab3fSAlgea Cao 		mdelay(100);
8138e2bab3fSAlgea Cao 	/* set pdata_en to 1 */
8148e2bab3fSAlgea Cao 	inno_update_bits(inno, 0x02, 1, 1);
8158e2bab3fSAlgea Cao 
8168e2bab3fSAlgea Cao 	return 0;
8178e2bab3fSAlgea Cao }
8188e2bab3fSAlgea Cao 
inno_hdmi_phy_rk3328_power_off(struct inno_hdmi_phy * inno)8198e2bab3fSAlgea Cao static void inno_hdmi_phy_rk3328_power_off(struct inno_hdmi_phy *inno)
8208e2bab3fSAlgea Cao {
8218e2bab3fSAlgea Cao 	/* Power off driver */
8228e2bab3fSAlgea Cao 	inno_write(inno, 0xb2, 0);
8238e2bab3fSAlgea Cao 	/* Power off band gap */
8248e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xb0, 4, 0);
8258e2bab3fSAlgea Cao 	/* Power off post pll */
8268e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xaa, 1, 1);
8278e2bab3fSAlgea Cao }
8288e2bab3fSAlgea Cao 
8298e2bab3fSAlgea Cao static int
inno_hdmi_phy_rk3328_pre_pll_update(struct inno_hdmi_phy * inno,const struct pre_pll_config * cfg)8308e2bab3fSAlgea Cao inno_hdmi_phy_rk3328_pre_pll_update(struct inno_hdmi_phy *inno,
8318e2bab3fSAlgea Cao 				    const struct pre_pll_config *cfg)
8328e2bab3fSAlgea Cao {
8338e2bab3fSAlgea Cao 	u32 val;
8348e2bab3fSAlgea Cao 
8358e2bab3fSAlgea Cao 	/* Power off PLL */
8368e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xa0, 1, 1);
8378e2bab3fSAlgea Cao 	/* Configure pre-pll */
8388e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xa0, 2, (cfg->vco_div_5_en & 1) << 1);
8398e2bab3fSAlgea Cao 	inno_write(inno, 0xa1, cfg->prediv);
8408e2bab3fSAlgea Cao 	if (cfg->fracdiv)
8418e2bab3fSAlgea Cao 		val = ((cfg->fbdiv >> 8) & 0x0f) | 0xc0;
8428e2bab3fSAlgea Cao 	else
8438e2bab3fSAlgea Cao 		val = ((cfg->fbdiv >> 8) & 0x0f) | 0xf0;
8448e2bab3fSAlgea Cao 	inno_write(inno, 0xa2, val);
8458e2bab3fSAlgea Cao 	inno_write(inno, 0xa3, cfg->fbdiv & 0xff);
8468e2bab3fSAlgea Cao 	val = (cfg->pclk_div_a & 0x1f) |
8478e2bab3fSAlgea Cao 	      ((cfg->pclk_div_b & 3) << 5);
8488e2bab3fSAlgea Cao 	inno_write(inno, 0xa5, val);
8498e2bab3fSAlgea Cao 	val = (cfg->pclk_div_d & 0x1f) |
8508e2bab3fSAlgea Cao 	      ((cfg->pclk_div_c & 3) << 5);
8518e2bab3fSAlgea Cao 	inno_write(inno, 0xa6, val);
8528e2bab3fSAlgea Cao 	val = ((cfg->tmds_div_a & 3) << 4) |
8538e2bab3fSAlgea Cao 	      ((cfg->tmds_div_b & 3) << 2) |
8548e2bab3fSAlgea Cao 	      (cfg->tmds_div_c & 3);
8558e2bab3fSAlgea Cao 	inno_write(inno, 0xa4, val);
8568e2bab3fSAlgea Cao 
8578e2bab3fSAlgea Cao 	if (cfg->fracdiv) {
8588e2bab3fSAlgea Cao 		val = cfg->fracdiv & 0xff;
8598e2bab3fSAlgea Cao 		inno_write(inno, 0xd3, val);
8608e2bab3fSAlgea Cao 		val = (cfg->fracdiv >> 8) & 0xff;
8618e2bab3fSAlgea Cao 		inno_write(inno, 0xd2, val);
8628e2bab3fSAlgea Cao 		val = (cfg->fracdiv >> 16) & 0xff;
8638e2bab3fSAlgea Cao 		inno_write(inno, 0xd1, val);
8648e2bab3fSAlgea Cao 	} else {
8658e2bab3fSAlgea Cao 		inno_write(inno, 0xd3, 0);
8668e2bab3fSAlgea Cao 		inno_write(inno, 0xd2, 0);
8678e2bab3fSAlgea Cao 		inno_write(inno, 0xd1, 0);
8688e2bab3fSAlgea Cao 	}
8698e2bab3fSAlgea Cao 
8708e2bab3fSAlgea Cao 	/* Power up PLL */
8718e2bab3fSAlgea Cao 	inno_update_bits(inno, 0xa0, 1, 0);
8728e2bab3fSAlgea Cao 
8738e2bab3fSAlgea Cao 	/* Wait for PLL lock */
8748e2bab3fSAlgea Cao 	for (val = 0; val < 5; val++) {
8758e2bab3fSAlgea Cao 		if (inno_read(inno, 0xa9) & 1)
8768e2bab3fSAlgea Cao 			break;
8778e2bab3fSAlgea Cao 		udelay(1000);
8788e2bab3fSAlgea Cao 	}
8798e2bab3fSAlgea Cao 	if (val == 5) {
8808e2bab3fSAlgea Cao 		printf("Pre-PLL unlock\n");
8818e2bab3fSAlgea Cao 		return -ETIMEDOUT;
8828e2bab3fSAlgea Cao 	}
8838e2bab3fSAlgea Cao 
8848e2bab3fSAlgea Cao 	return 0;
8858e2bab3fSAlgea Cao }
8868e2bab3fSAlgea Cao 
8878e2bab3fSAlgea Cao static unsigned long
inno_hdmi_3328_phy_pll_recalc_rate(struct inno_hdmi_phy * inno,unsigned long parent_rate)8888e2bab3fSAlgea Cao inno_hdmi_3328_phy_pll_recalc_rate(struct inno_hdmi_phy *inno,
8898e2bab3fSAlgea Cao 				   unsigned long parent_rate)
8908e2bab3fSAlgea Cao {
8918e2bab3fSAlgea Cao 	unsigned long rate, vco, frac;
8928e2bab3fSAlgea Cao 	u8 nd, no_a, no_b, no_d;
8938e2bab3fSAlgea Cao 	__maybe_unused u8 no_c;
8948e2bab3fSAlgea Cao 	u16 nf;
8958e2bab3fSAlgea Cao 
8968e2bab3fSAlgea Cao 	nd = inno_read(inno, 0xa1) & 0x3f;
8978e2bab3fSAlgea Cao 	nf = ((inno_read(inno, 0xa2) & 0x0f) << 8) | inno_read(inno, 0xa3);
8988e2bab3fSAlgea Cao 	vco = parent_rate * nf;
8998e2bab3fSAlgea Cao 	if ((inno_read(inno, 0xa2) & 0x30) == 0) {
9008e2bab3fSAlgea Cao 		frac = inno_read(inno, 0xd3) |
9018e2bab3fSAlgea Cao 		       (inno_read(inno, 0xd2) << 8) |
9028e2bab3fSAlgea Cao 		       (inno_read(inno, 0xd1) << 16);
9038e2bab3fSAlgea Cao 		vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
9048e2bab3fSAlgea Cao 	}
9058e2bab3fSAlgea Cao 	if (inno_read(inno, 0xa0) & 2) {
9068e2bab3fSAlgea Cao 		rate = vco / (nd * 5);
9078e2bab3fSAlgea Cao 	} else {
9088e2bab3fSAlgea Cao 		no_a = inno_read(inno, 0xa5) & 0x1f;
9098e2bab3fSAlgea Cao 		no_b = ((inno_read(inno, 0xa5) >> 5) & 7) + 2;
9108e2bab3fSAlgea Cao 		no_c = (1 << ((inno_read(inno, 0xa6) >> 5) & 7));
9118e2bab3fSAlgea Cao 		no_d = inno_read(inno, 0xa6) & 0x1f;
9128e2bab3fSAlgea Cao 		if (no_a == 1)
9138e2bab3fSAlgea Cao 			rate = vco / (nd * no_b * no_d * 2);
9148e2bab3fSAlgea Cao 		else
9158e2bab3fSAlgea Cao 			rate = vco / (nd * no_a * no_d * 2);
9168e2bab3fSAlgea Cao 	}
9178e2bab3fSAlgea Cao 	inno->pixclock = rate;
9188e2bab3fSAlgea Cao 
9198e2bab3fSAlgea Cao 	return rate;
9208e2bab3fSAlgea Cao }
9218e2bab3fSAlgea Cao 
92219957ff8SAlgea Cao static int
inno_hdmi_phy_rk3528_power_on(struct inno_hdmi_phy * inno,const struct post_pll_config * cfg,const struct phy_config * phy_cfg)92319957ff8SAlgea Cao inno_hdmi_phy_rk3528_power_on(struct inno_hdmi_phy *inno,
92419957ff8SAlgea Cao 			      const struct post_pll_config *cfg,
92519957ff8SAlgea Cao 			      const struct phy_config *phy_cfg)
92619957ff8SAlgea Cao {
92719957ff8SAlgea Cao 	u32 val;
92819957ff8SAlgea Cao 	u64 temp;
929268b9134SAlgea Cao 	u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock);
93019957ff8SAlgea Cao 
93119957ff8SAlgea Cao 	/* Power off post PLL */
93279d74bcfSAlgea Cao 	inno_update_bits(inno, 0xaa, 1, 0);
93319957ff8SAlgea Cao 
93419957ff8SAlgea Cao 	val = cfg->prediv;
93519957ff8SAlgea Cao 	inno_write(inno, 0xab, val);
93619957ff8SAlgea Cao 
93719957ff8SAlgea Cao 	if (cfg->postdiv == 1) {
938cb24dc0eSAlgea Cao 		inno_write(inno, 0xad, 0x8);
93919957ff8SAlgea Cao 		inno_write(inno, 0xaa, 2);
94019957ff8SAlgea Cao 	} else {
94119957ff8SAlgea Cao 		val = (cfg->postdiv / 2) - 1;
94219957ff8SAlgea Cao 		inno_write(inno, 0xad, val);
94319957ff8SAlgea Cao 		inno_write(inno, 0xaa, 0x0e);
94419957ff8SAlgea Cao 	}
94519957ff8SAlgea Cao 
94619957ff8SAlgea Cao 	val = cfg->fbdiv & 0xff;
94719957ff8SAlgea Cao 	inno_write(inno, 0xac, val);
94819957ff8SAlgea Cao 	val = (cfg->fbdiv >> 8) & BIT(0);
94919957ff8SAlgea Cao 	inno_update_bits(inno, 0xad, BIT(4), val);
95019957ff8SAlgea Cao 
95119957ff8SAlgea Cao 	/* current bias clk/data 2 */
95219957ff8SAlgea Cao 	val = phy_cfg->regs[0] << 4 | phy_cfg->regs[1];
95319957ff8SAlgea Cao 	inno_write(inno, 0xbf, val);
95419957ff8SAlgea Cao 
95519957ff8SAlgea Cao 	/* current bias data 1/0 */
95619957ff8SAlgea Cao 	val = phy_cfg->regs[1] << 4 | phy_cfg->regs[1];
95719957ff8SAlgea Cao 	inno_write(inno, 0xc0, val);
95819957ff8SAlgea Cao 
95919957ff8SAlgea Cao 	/* output voltage */
96019957ff8SAlgea Cao 	inno_write(inno, 0xb5, phy_cfg->regs[2]);
96119957ff8SAlgea Cao 	inno_write(inno, 0xb6, phy_cfg->regs[3]);
96219957ff8SAlgea Cao 	inno_write(inno, 0xb7, phy_cfg->regs[3]);
96319957ff8SAlgea Cao 	inno_write(inno, 0xb8, phy_cfg->regs[3]);
96419957ff8SAlgea Cao 
96519957ff8SAlgea Cao 	/* pre-emphasis */
96619957ff8SAlgea Cao 	inno_write(inno, 0xbb, phy_cfg->regs[4]);
96719957ff8SAlgea Cao 	inno_write(inno, 0xbc, phy_cfg->regs[4]);
96819957ff8SAlgea Cao 	inno_write(inno, 0xbd, phy_cfg->regs[4]);
96919957ff8SAlgea Cao 
97019957ff8SAlgea Cao 	/* enable LDO */
97119957ff8SAlgea Cao 	inno_write(inno, 0xb4, 0x7);
97219957ff8SAlgea Cao 
97319957ff8SAlgea Cao 	/* enable serializer */
974cb24dc0eSAlgea Cao 	inno_write(inno, 0xbe, 0x70);
97519957ff8SAlgea Cao 
97679d74bcfSAlgea Cao 	inno_write(inno, 0xb2, 0x0f);
97779d74bcfSAlgea Cao 
97879d74bcfSAlgea Cao 	for (val = 0; val < 5; val++) {
97979d74bcfSAlgea Cao 		if (inno_read(inno, 0xaf) & 1)
98079d74bcfSAlgea Cao 			break;
98179d74bcfSAlgea Cao 		udelay(1000);
98279d74bcfSAlgea Cao 	}
98379d74bcfSAlgea Cao 	if (!(inno_read(inno, 0xaf) & 1)) {
98479d74bcfSAlgea Cao 		dev_err(inno->dev, "HDMI PHY Post PLL unlock\n");
98579d74bcfSAlgea Cao 		return -ETIMEDOUT;
98679d74bcfSAlgea Cao 	}
98779d74bcfSAlgea Cao 
988268b9134SAlgea Cao 	/* set termination resistance */
989*27c6606cSAlgea Cao 	inno_write(inno, 0xc7, phy_cfg->regs[5]);
990*27c6606cSAlgea Cao 	inno_write(inno, 0xc5, phy_cfg->regs[6]);
991*27c6606cSAlgea Cao 	inno_write(inno, 0xc8, phy_cfg->regs[7]);
992*27c6606cSAlgea Cao 	inno_write(inno, 0xc9, phy_cfg->regs[8]);
993*27c6606cSAlgea Cao 	inno_write(inno, 0xca, phy_cfg->regs[8]);
994*27c6606cSAlgea Cao 	inno_write(inno, 0xcb, phy_cfg->regs[8]);
99519957ff8SAlgea Cao 
99619957ff8SAlgea Cao 
99719957ff8SAlgea Cao 	/* set TMDS sync detection counter length */
998268b9134SAlgea Cao 	temp = 47520000000UL / tmdsclock;
99919957ff8SAlgea Cao 	inno_write(inno, 0xd8, (temp >> 8) & 0xff);
100019957ff8SAlgea Cao 	inno_write(inno, 0xd9, temp & 0xff);
100119957ff8SAlgea Cao 
100219957ff8SAlgea Cao 	if (phy_cfg->tmdsclock > 340000000)
100319957ff8SAlgea Cao 		mdelay(100);
100479d74bcfSAlgea Cao 	/* set pdata_en to 0/1 */
100579d74bcfSAlgea Cao 	inno_update_bits(inno, 0x02, 1, 0);
100619957ff8SAlgea Cao 	inno_update_bits(inno, 0x02, 1, 1);
100719957ff8SAlgea Cao 
100819957ff8SAlgea Cao 	/* Enable PHY IRQ */
100919957ff8SAlgea Cao 	inno_write(inno, 0x05, 0x22);
101019957ff8SAlgea Cao 	inno_write(inno, 0x07, 0x22);
1011cb24dc0eSAlgea Cao 	inno_write(inno, 0xcc, 0x0f);
1012cb24dc0eSAlgea Cao 
101319957ff8SAlgea Cao 	return 0;
101419957ff8SAlgea Cao }
101519957ff8SAlgea Cao 
inno_hdmi_phy_rk3528_power_off(struct inno_hdmi_phy * inno)101619957ff8SAlgea Cao static void inno_hdmi_phy_rk3528_power_off(struct inno_hdmi_phy *inno)
101719957ff8SAlgea Cao {
101819957ff8SAlgea Cao 	/* Power off driver */
101919957ff8SAlgea Cao 	inno_write(inno, 0xb2, 0);
102019957ff8SAlgea Cao 	/* Power off band gap */
102119957ff8SAlgea Cao 	inno_update_bits(inno, 0xb0, 4, 0);
102219957ff8SAlgea Cao 	/* Power off post pll */
102319957ff8SAlgea Cao 	inno_update_bits(inno, 0xaa, 1, 1);
102419957ff8SAlgea Cao 
102519957ff8SAlgea Cao 	/* Disable PHY IRQ */
102619957ff8SAlgea Cao 	inno_write(inno, 0x05, 0);
102719957ff8SAlgea Cao 	inno_write(inno, 0x07, 0);
102819957ff8SAlgea Cao }
102919957ff8SAlgea Cao 
inno_hdmi_phy_rk3528_init(struct inno_hdmi_phy * inno)103019957ff8SAlgea Cao static void inno_hdmi_phy_rk3528_init(struct inno_hdmi_phy *inno)
103119957ff8SAlgea Cao {
103219957ff8SAlgea Cao 	/*
103319957ff8SAlgea Cao 	 * Use phy internal register control
103419957ff8SAlgea Cao 	 * rxsense/poweron/pllpd/pdataen signal.
103519957ff8SAlgea Cao 	 */
103619957ff8SAlgea Cao 	inno_write(inno, 0x02, 0x81);
103719957ff8SAlgea Cao }
103819957ff8SAlgea Cao 
103919957ff8SAlgea Cao static int
inno_hdmi_phy_rk3528_pre_pll_update(struct inno_hdmi_phy * inno,const struct pre_pll_config * cfg)104019957ff8SAlgea Cao inno_hdmi_phy_rk3528_pre_pll_update(struct inno_hdmi_phy *inno,
104119957ff8SAlgea Cao 				    const struct pre_pll_config *cfg)
104219957ff8SAlgea Cao {
104319957ff8SAlgea Cao 	u32 val;
104419957ff8SAlgea Cao 
104579d74bcfSAlgea Cao 	inno_update_bits(inno, 0xb0, 4, 4);
104679d74bcfSAlgea Cao 	inno_write(inno, 0xcc, 0x0f);
104779d74bcfSAlgea Cao 
104879d74bcfSAlgea Cao 	/* Power on PLL */
104979d74bcfSAlgea Cao 	inno_update_bits(inno, 0xa0, 1, 0);
105019957ff8SAlgea Cao 	/* Configure pre-pll */
105119957ff8SAlgea Cao 	inno_update_bits(inno, 0xa0, 2, (cfg->vco_div_5_en & 1) << 1);
105219957ff8SAlgea Cao 	inno_write(inno, 0xa1, cfg->prediv);
105319957ff8SAlgea Cao 	if (cfg->fracdiv)
105419957ff8SAlgea Cao 		val = ((cfg->fbdiv >> 8) & 0x0f) | 0xc0;
105519957ff8SAlgea Cao 	else
105619957ff8SAlgea Cao 		val = ((cfg->fbdiv >> 8) & 0x0f) | 0xf0;
105719957ff8SAlgea Cao 	inno_write(inno, 0xa2, val);
105819957ff8SAlgea Cao 	inno_write(inno, 0xa3, cfg->fbdiv & 0xff);
105919957ff8SAlgea Cao 	val = (cfg->pclk_div_a & 0x1f) |
106019957ff8SAlgea Cao 	      ((cfg->pclk_div_b & 3) << 5);
106119957ff8SAlgea Cao 	inno_write(inno, 0xa5, val);
106219957ff8SAlgea Cao 	val = (cfg->pclk_div_d & 0x1f) |
106319957ff8SAlgea Cao 	      ((cfg->pclk_div_c & 3) << 5);
106419957ff8SAlgea Cao 	inno_write(inno, 0xa6, val);
106519957ff8SAlgea Cao 	val = ((cfg->tmds_div_a & 3) << 4) |
106619957ff8SAlgea Cao 	      ((cfg->tmds_div_b & 3) << 2) |
106719957ff8SAlgea Cao 	      (cfg->tmds_div_c & 3);
106819957ff8SAlgea Cao 	inno_write(inno, 0xa4, val);
106919957ff8SAlgea Cao 
107019957ff8SAlgea Cao 	if (cfg->fracdiv) {
107119957ff8SAlgea Cao 		val = cfg->fracdiv & 0xff;
107219957ff8SAlgea Cao 		inno_write(inno, 0xd3, val);
107319957ff8SAlgea Cao 		val = (cfg->fracdiv >> 8) & 0xff;
107419957ff8SAlgea Cao 		inno_write(inno, 0xd2, val);
107519957ff8SAlgea Cao 		val = (cfg->fracdiv >> 16) & 0xff;
107619957ff8SAlgea Cao 		inno_write(inno, 0xd1, val);
107719957ff8SAlgea Cao 	} else {
107819957ff8SAlgea Cao 		inno_write(inno, 0xd3, 0);
107919957ff8SAlgea Cao 		inno_write(inno, 0xd2, 0);
108019957ff8SAlgea Cao 		inno_write(inno, 0xd1, 0);
108119957ff8SAlgea Cao 	}
108219957ff8SAlgea Cao 
108319957ff8SAlgea Cao 	/* Wait for PLL lock */
108419957ff8SAlgea Cao 	for (val = 0; val < 5; val++) {
108519957ff8SAlgea Cao 		if (inno_read(inno, 0xa9) & 1)
108619957ff8SAlgea Cao 			break;
108719957ff8SAlgea Cao 		udelay(1000);
108819957ff8SAlgea Cao 	}
108919957ff8SAlgea Cao 	if (val == 5) {
109019957ff8SAlgea Cao 		dev_err(inno->dev, "Pre-PLL unlock\n");
109119957ff8SAlgea Cao 		return -ETIMEDOUT;
109219957ff8SAlgea Cao 	}
109319957ff8SAlgea Cao 
109419957ff8SAlgea Cao 	return 0;
109519957ff8SAlgea Cao }
109619957ff8SAlgea Cao 
109719957ff8SAlgea Cao static unsigned long
inno_hdmi_rk3528_phy_pll_recalc_rate(struct inno_hdmi_phy * inno,unsigned long parent_rate)109819957ff8SAlgea Cao inno_hdmi_rk3528_phy_pll_recalc_rate(struct inno_hdmi_phy *inno,
109919957ff8SAlgea Cao 				     unsigned long parent_rate)
110019957ff8SAlgea Cao {
110119957ff8SAlgea Cao 	unsigned long frac;
110219957ff8SAlgea Cao 	u8 nd, no_a, no_b, no_d;
110319957ff8SAlgea Cao 	u16 nf;
110419957ff8SAlgea Cao 	u64 vco = parent_rate;
110519957ff8SAlgea Cao 
110619957ff8SAlgea Cao 	nd = inno_read(inno, 0xa1) & 0x3f;
110719957ff8SAlgea Cao 	nf = ((inno_read(inno, 0xa2) & 0x0f) << 8) | inno_read(inno, 0xa3);
110819957ff8SAlgea Cao 	vco *= nf;
110919957ff8SAlgea Cao 	if ((inno_read(inno, 0xa2) & 0x30) == 0) {
111019957ff8SAlgea Cao 		frac = inno_read(inno, 0xd3) |
111119957ff8SAlgea Cao 		       (inno_read(inno, 0xd2) << 8) |
111219957ff8SAlgea Cao 		       (inno_read(inno, 0xd1) << 16);
111319957ff8SAlgea Cao 		vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
111419957ff8SAlgea Cao 	}
111519957ff8SAlgea Cao 	if (inno_read(inno, 0xa0) & 2) {
111619957ff8SAlgea Cao 		do_div(vco, nd * 5);
111719957ff8SAlgea Cao 	} else {
111819957ff8SAlgea Cao 		no_a = inno_read(inno, 0xa5) & 0x1f;
111919957ff8SAlgea Cao 		no_b = ((inno_read(inno, 0xa5) >> 5) & 7) + 2;
112019957ff8SAlgea Cao 		no_d = inno_read(inno, 0xa6) & 0x1f;
112119957ff8SAlgea Cao 		if (no_a == 1)
112219957ff8SAlgea Cao 			do_div(vco, nd * no_b * no_d * 2);
112319957ff8SAlgea Cao 		else
112419957ff8SAlgea Cao 			do_div(vco, nd * no_a * no_d * 2);
112519957ff8SAlgea Cao 	}
112619957ff8SAlgea Cao 
112719957ff8SAlgea Cao 	frac = vco;
112819957ff8SAlgea Cao 	inno->pixclock = DIV_ROUND_CLOSEST(frac, 1000) * 1000;
112919957ff8SAlgea Cao 
113019957ff8SAlgea Cao 	dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock);
113119957ff8SAlgea Cao 
113219957ff8SAlgea Cao 	return frac;
113319957ff8SAlgea Cao }
113419957ff8SAlgea Cao 
11356a3f4548SSandy Huang #ifndef CONFIG_SPL_BUILD
11368e2bab3fSAlgea Cao #define PHY_TAB_LEN 60
11378e2bab3fSAlgea Cao 
11388e2bab3fSAlgea Cao static
inno_hdmi_update_phy_table(struct inno_hdmi_phy * inno,u32 * config,struct phy_config * phy_cfg,int phy_table_size)11398e2bab3fSAlgea Cao int inno_hdmi_update_phy_table(struct inno_hdmi_phy *inno, u32 *config,
11408e2bab3fSAlgea Cao 			       struct phy_config *phy_cfg,
11418e2bab3fSAlgea Cao 			       int phy_table_size)
11428e2bab3fSAlgea Cao {
11438e2bab3fSAlgea Cao 	int i, j;
11448e2bab3fSAlgea Cao 
11458e2bab3fSAlgea Cao 	for (i = 0; i < phy_table_size; i++) {
11468e2bab3fSAlgea Cao 		phy_cfg[i].tmdsclock =
11478e2bab3fSAlgea Cao 			(unsigned long)config[i * 15];
11488e2bab3fSAlgea Cao 
11498e2bab3fSAlgea Cao 		debug("%ld ", phy_cfg[i].tmdsclock);
11508e2bab3fSAlgea Cao 		for (j = 0; j < 14; j++) {
11518e2bab3fSAlgea Cao 			phy_cfg[i].regs[j] = (u8)config[i * 15 + 1 + j];
11528e2bab3fSAlgea Cao 			debug("0x%02x ", phy_cfg[i].regs[j]);
11538e2bab3fSAlgea Cao 		}
11548e2bab3fSAlgea Cao 		debug("\n");
11558e2bab3fSAlgea Cao 	}
11568e2bab3fSAlgea Cao 
11578e2bab3fSAlgea Cao 	/*
11588e2bab3fSAlgea Cao 	 * The last set of phy cfg is used to indicate whether
11598e2bab3fSAlgea Cao 	 * there is no more phy cfg data.
11608e2bab3fSAlgea Cao 	 */
11618e2bab3fSAlgea Cao 	phy_cfg[i].tmdsclock = ~0UL;
11628e2bab3fSAlgea Cao 	for (j = 0; j < 14; j++)
11638e2bab3fSAlgea Cao 		phy_cfg[i].regs[j] = 0;
11648e2bab3fSAlgea Cao 
11658e2bab3fSAlgea Cao 	return 0;
11668e2bab3fSAlgea Cao }
11676a3f4548SSandy Huang #endif
11688e2bab3fSAlgea Cao 
11698e2bab3fSAlgea Cao static const struct inno_hdmi_phy_ops rk3228_hdmi_phy_ops = {
11708e2bab3fSAlgea Cao 	.init = inno_hdmi_phy_rk3228_init,
11718e2bab3fSAlgea Cao 	.power_on = inno_hdmi_phy_rk3228_power_on,
11728e2bab3fSAlgea Cao 	.power_off = inno_hdmi_phy_rk3228_power_off,
11738e2bab3fSAlgea Cao 	.pre_pll_update = inno_hdmi_phy_rk3228_pre_pll_update,
11748e2bab3fSAlgea Cao };
11758e2bab3fSAlgea Cao 
11768e2bab3fSAlgea Cao static const struct inno_hdmi_phy_ops rk3328_hdmi_phy_ops = {
11778e2bab3fSAlgea Cao 	.init = inno_hdmi_phy_rk3328_init,
11788e2bab3fSAlgea Cao 	.power_on = inno_hdmi_phy_rk3328_power_on,
11798e2bab3fSAlgea Cao 	.power_off = inno_hdmi_phy_rk3328_power_off,
11808e2bab3fSAlgea Cao 	.pre_pll_update = inno_hdmi_phy_rk3328_pre_pll_update,
11818e2bab3fSAlgea Cao 	.recalc_rate = inno_hdmi_3328_phy_pll_recalc_rate,
11828e2bab3fSAlgea Cao };
11838e2bab3fSAlgea Cao 
118419957ff8SAlgea Cao static const struct inno_hdmi_phy_ops rk3528_hdmi_phy_ops = {
118519957ff8SAlgea Cao 	.init = inno_hdmi_phy_rk3528_init,
118619957ff8SAlgea Cao 	.power_on = inno_hdmi_phy_rk3528_power_on,
118719957ff8SAlgea Cao 	.power_off = inno_hdmi_phy_rk3528_power_off,
118819957ff8SAlgea Cao 	.pre_pll_update = inno_hdmi_phy_rk3528_pre_pll_update,
118919957ff8SAlgea Cao 	.recalc_rate = inno_hdmi_rk3528_phy_pll_recalc_rate,
119019957ff8SAlgea Cao };
119119957ff8SAlgea Cao 
11928e2bab3fSAlgea Cao static const struct inno_hdmi_phy_drv_data rk3228_hdmi_phy_drv_data = {
11938e2bab3fSAlgea Cao 	.dev_type = INNO_HDMI_PHY_RK3228,
11948e2bab3fSAlgea Cao 	.ops = &rk3228_hdmi_phy_ops,
11958e2bab3fSAlgea Cao 	.phy_cfg_table = rk3228_phy_cfg,
11968e2bab3fSAlgea Cao };
11978e2bab3fSAlgea Cao 
11988e2bab3fSAlgea Cao static const struct inno_hdmi_phy_drv_data rk3328_hdmi_phy_drv_data = {
11998e2bab3fSAlgea Cao 	.dev_type = INNO_HDMI_PHY_RK3328,
12008e2bab3fSAlgea Cao 	.ops = &rk3328_hdmi_phy_ops,
12018e2bab3fSAlgea Cao 	.phy_cfg_table = rk3328_phy_cfg,
12028e2bab3fSAlgea Cao };
12038e2bab3fSAlgea Cao 
120419957ff8SAlgea Cao static const struct inno_hdmi_phy_drv_data rk3528_hdmi_phy_drv_data = {
120519957ff8SAlgea Cao 	.dev_type = INNO_HDMI_PHY_RK3528,
120619957ff8SAlgea Cao 	.ops = &rk3528_hdmi_phy_ops,
120719957ff8SAlgea Cao 	.phy_cfg_table = rk3528_phy_cfg,
120819957ff8SAlgea Cao };
120919957ff8SAlgea Cao 
12108e2bab3fSAlgea Cao static const struct rockchip_inno_data inno_hdmi_phy_of_match[] = {
12118e2bab3fSAlgea Cao 	{ .compatible = "rockchip,rk3228-hdmi-phy",
12128e2bab3fSAlgea Cao 	  .data = &rk3228_hdmi_phy_drv_data
12138e2bab3fSAlgea Cao 	},
12148e2bab3fSAlgea Cao 	{ .compatible = "rockchip,rk3328-hdmi-phy",
12158e2bab3fSAlgea Cao 	  .data = &rk3328_hdmi_phy_drv_data
12168e2bab3fSAlgea Cao 	},
121719957ff8SAlgea Cao 	{ .compatible = "rockchip,rk3528-hdmi-phy",
121819957ff8SAlgea Cao 	  .data = &rk3528_hdmi_phy_drv_data
121919957ff8SAlgea Cao 	},
12208e2bab3fSAlgea Cao 	{}
12218e2bab3fSAlgea Cao };
12228e2bab3fSAlgea Cao 
inno_hdmi_phy_init(struct rockchip_phy * phy)122315081c50SWyon Bi static int inno_hdmi_phy_init(struct rockchip_phy *phy)
12248e2bab3fSAlgea Cao {
12256a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
12266a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
12276a3f4548SSandy Huang #else
122815081c50SWyon Bi 	struct udevice *dev = phy->dev;
122915081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
12306a3f4548SSandy Huang 	int val, phy_table_size, ret;
12318e2bab3fSAlgea Cao 	u32 *phy_config;
12326a3f4548SSandy Huang #endif
12336a3f4548SSandy Huang 	int i;
12346a3f4548SSandy Huang 	const char *name;
12358e2bab3fSAlgea Cao 
12366a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
12376a3f4548SSandy Huang 	inno->regs = (void *)RK3528_HDMIPHY_BASE;
12386a3f4548SSandy Huang #else
12398e2bab3fSAlgea Cao 	inno->regs = dev_read_addr_ptr(dev);
12406a3f4548SSandy Huang 	inno->node = dev->node;
12416a3f4548SSandy Huang #endif
12428e2bab3fSAlgea Cao 	if (!inno->regs) {
12438e2bab3fSAlgea Cao 		printf("%s: failed to get phy address\n", __func__);
12448e2bab3fSAlgea Cao 		return -ENOMEM;
12458e2bab3fSAlgea Cao 	}
12468e2bab3fSAlgea Cao 
12476a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
12486a3f4548SSandy Huang 	name = "rockchip,rk3528-hdmi-phy";
12496a3f4548SSandy Huang #else
12508e2bab3fSAlgea Cao 	name = dev_read_string(dev, "compatible");
12516a3f4548SSandy Huang #endif
12528e2bab3fSAlgea Cao 	for (i = 0; i < ARRAY_SIZE(inno_hdmi_phy_of_match); i++) {
12538e2bab3fSAlgea Cao 		if (!strcmp(name, inno_hdmi_phy_of_match[i].compatible)) {
12548e2bab3fSAlgea Cao 			inno->plat_data = inno_hdmi_phy_of_match[i].data;
12558e2bab3fSAlgea Cao 			break;
12568e2bab3fSAlgea Cao 		}
12578e2bab3fSAlgea Cao 	}
12588e2bab3fSAlgea Cao 
12596a3f4548SSandy Huang #ifndef CONFIG_SPL_BUILD
12608e2bab3fSAlgea Cao 	dev_read_prop(dev, "rockchip,phy-table", &val);
12618e2bab3fSAlgea Cao 
12628e2bab3fSAlgea Cao 	if (val >= 0) {
12638e2bab3fSAlgea Cao 		if (val % PHY_TAB_LEN || !val) {
12648e2bab3fSAlgea Cao 			printf("Invalid phy cfg table format!\n");
12658e2bab3fSAlgea Cao 			return -EINVAL;
12668e2bab3fSAlgea Cao 		}
12678e2bab3fSAlgea Cao 
12688e2bab3fSAlgea Cao 		phy_config = malloc(val);
12698e2bab3fSAlgea Cao 		if (!phy_config) {
12708e2bab3fSAlgea Cao 			printf("kmalloc phy table failed\n");
12718e2bab3fSAlgea Cao 			return -ENOMEM;
12728e2bab3fSAlgea Cao 		}
12738e2bab3fSAlgea Cao 
12748e2bab3fSAlgea Cao 		phy_table_size = val / PHY_TAB_LEN;
12758e2bab3fSAlgea Cao 		/* Effective phy cfg data and the end of phy cfg table */
12768e2bab3fSAlgea Cao 		inno->phy_cfg = malloc(val + PHY_TAB_LEN);
12778e2bab3fSAlgea Cao 		if (!inno->phy_cfg) {
12788e2bab3fSAlgea Cao 			free(phy_config);
12798e2bab3fSAlgea Cao 			return -ENOMEM;
12808e2bab3fSAlgea Cao 		}
12818e2bab3fSAlgea Cao 
12828e2bab3fSAlgea Cao 		dev_read_u32_array(dev, "rockchip,phy-table",
12838e2bab3fSAlgea Cao 				   phy_config, val / sizeof(u32));
12848e2bab3fSAlgea Cao 		ret = inno_hdmi_update_phy_table(inno, phy_config,
12858e2bab3fSAlgea Cao 						 inno->phy_cfg,
12868e2bab3fSAlgea Cao 						 phy_table_size);
12878e2bab3fSAlgea Cao 		if (ret) {
12888e2bab3fSAlgea Cao 			free(phy_config);
12898e2bab3fSAlgea Cao 			return ret;
12908e2bab3fSAlgea Cao 		}
12918e2bab3fSAlgea Cao 		free(phy_config);
12928e2bab3fSAlgea Cao 	} else {
12938e2bab3fSAlgea Cao 		printf("use default hdmi phy table\n");
12948e2bab3fSAlgea Cao 	}
12956a3f4548SSandy Huang #endif
12968e2bab3fSAlgea Cao 
12978e2bab3fSAlgea Cao 	if (i >= ARRAY_SIZE(inno_hdmi_phy_of_match))
12988e2bab3fSAlgea Cao 		return 0;
12998e2bab3fSAlgea Cao 
13008e2bab3fSAlgea Cao 	if (!inno->plat_data || !inno->plat_data->ops)
13018e2bab3fSAlgea Cao 		return -EINVAL;
13028e2bab3fSAlgea Cao 
13038e2bab3fSAlgea Cao 	if (inno->plat_data->ops->init)
13048e2bab3fSAlgea Cao 		inno->plat_data->ops->init(inno);
13058e2bab3fSAlgea Cao 
13068e2bab3fSAlgea Cao 	return 0;
13078e2bab3fSAlgea Cao }
13088e2bab3fSAlgea Cao 
inno_hdmi_phy_set_pll(struct rockchip_phy * phy,unsigned long rate)130915081c50SWyon Bi static unsigned long inno_hdmi_phy_set_pll(struct rockchip_phy *phy,
13108e2bab3fSAlgea Cao 					   unsigned long rate)
13118e2bab3fSAlgea Cao {
13126a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
13136a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
13146a3f4548SSandy Huang #else
131515081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
13166a3f4548SSandy Huang #endif
131715081c50SWyon Bi 
13186a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
13196a3f4548SSandy Huang 	if (!inno)
13206a3f4548SSandy Huang 		inno = g_inno;
13216a3f4548SSandy Huang #endif
132215081c50SWyon Bi 	inno_hdmi_phy_clk_prepare(inno);
132315081c50SWyon Bi 	inno_hdmi_phy_clk_is_prepared(inno);
132415081c50SWyon Bi 	inno_hdmi_phy_clk_set_rate(inno, rate);
13258e2bab3fSAlgea Cao 	return 0;
13268e2bab3fSAlgea Cao }
13278e2bab3fSAlgea Cao 
132815081c50SWyon Bi static int
inno_hdmi_phy_set_bus_width(struct rockchip_phy * phy,u32 bus_width)132915081c50SWyon Bi inno_hdmi_phy_set_bus_width(struct rockchip_phy *phy, u32 bus_width)
13308e2bab3fSAlgea Cao {
13316a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
13326a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
13336a3f4548SSandy Huang #else
133415081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
13356a3f4548SSandy Huang #endif
13368e2bab3fSAlgea Cao 
13378e2bab3fSAlgea Cao 	inno->bus_width = bus_width;
133815081c50SWyon Bi 
133915081c50SWyon Bi 	return 0;
13408e2bab3fSAlgea Cao }
13418e2bab3fSAlgea Cao 
13428e2bab3fSAlgea Cao static long
inno_hdmi_phy_clk_round_rate(struct rockchip_phy * phy,unsigned long rate)134315081c50SWyon Bi inno_hdmi_phy_clk_round_rate(struct rockchip_phy *phy, unsigned long rate)
13448e2bab3fSAlgea Cao {
13456a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
13466a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
13476a3f4548SSandy Huang #else
134815081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
13496a3f4548SSandy Huang #endif
13508e2bab3fSAlgea Cao 	int i;
13518e2bab3fSAlgea Cao 	const struct pre_pll_config *cfg = pre_pll_cfg_table;
13528e2bab3fSAlgea Cao 	u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
13538e2bab3fSAlgea Cao 
13548e2bab3fSAlgea Cao 	for (; cfg->pixclock != ~0UL; cfg++)
13558e2bab3fSAlgea Cao 		if (cfg->pixclock == rate)
13568e2bab3fSAlgea Cao 			break;
13578e2bab3fSAlgea Cao 
13588e2bab3fSAlgea Cao 	/*
13598e2bab3fSAlgea Cao 	 * XXX: Limit pixel clock under 600MHz
13608e2bab3fSAlgea Cao 	 * rk3228 does not support non-zero fracdiv
13618e2bab3fSAlgea Cao 	 */
13628e2bab3fSAlgea Cao 	if ((inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228 &&
13638e2bab3fSAlgea Cao 	     cfg->fracdiv) || cfg->pixclock > 600000000)
13648e2bab3fSAlgea Cao 		return -EINVAL;
13658e2bab3fSAlgea Cao 
13668e2bab3fSAlgea Cao 	/*
13678e2bab3fSAlgea Cao 	 * If there is no dts phy cfg table, use default phy cfg table.
13688e2bab3fSAlgea Cao 	 * The tmds clock maximum is 594MHz. So there is no need to check
13698e2bab3fSAlgea Cao 	 * whether tmds clock is out of range.
13708e2bab3fSAlgea Cao 	 */
13718e2bab3fSAlgea Cao 	if (!inno->phy_cfg)
13728e2bab3fSAlgea Cao 		return cfg->pixclock;
13738e2bab3fSAlgea Cao 
13748e2bab3fSAlgea Cao 	/* Check if tmds clock is out of dts phy config's range. */
13758e2bab3fSAlgea Cao 	for (i = 0; inno->phy_cfg[i].tmdsclock != ~0UL; i++) {
13768e2bab3fSAlgea Cao 		if (inno->phy_cfg[i].tmdsclock >= tmdsclock)
13778e2bab3fSAlgea Cao 			break;
13788e2bab3fSAlgea Cao 	}
13798e2bab3fSAlgea Cao 
13808e2bab3fSAlgea Cao 	if (inno->phy_cfg[i].tmdsclock == ~0UL)
13818e2bab3fSAlgea Cao 		return -EINVAL;
13828e2bab3fSAlgea Cao 
13838e2bab3fSAlgea Cao 	return cfg->pixclock;
13848e2bab3fSAlgea Cao }
13858e2bab3fSAlgea Cao 
13868e2bab3fSAlgea Cao const struct rockchip_phy_funcs inno_hdmi_phy_funcs = {
13878e2bab3fSAlgea Cao 	.init = inno_hdmi_phy_init,
13888e2bab3fSAlgea Cao 	.power_on = inno_hdmi_phy_power_on,
13898e2bab3fSAlgea Cao 	.power_off = inno_hdmi_phy_power_off,
13908e2bab3fSAlgea Cao 	.set_pll = inno_hdmi_phy_set_pll,
13918e2bab3fSAlgea Cao 	.set_bus_width = inno_hdmi_phy_set_bus_width,
13928e2bab3fSAlgea Cao 	.round_rate = inno_hdmi_phy_clk_round_rate,
13938e2bab3fSAlgea Cao };
139415081c50SWyon Bi 
139515081c50SWyon Bi static struct rockchip_phy inno_hdmi_phy_driver_data = {
139615081c50SWyon Bi 	 .funcs = &inno_hdmi_phy_funcs,
139715081c50SWyon Bi };
139815081c50SWyon Bi 
139915081c50SWyon Bi static const struct udevice_id inno_hdmi_phy_ids[] = {
140015081c50SWyon Bi 	{
140115081c50SWyon Bi 	 .compatible = "rockchip,rk3328-hdmi-phy",
140215081c50SWyon Bi 	 .data = (ulong)&inno_hdmi_phy_driver_data,
140315081c50SWyon Bi 	},
140415081c50SWyon Bi 	{
140515081c50SWyon Bi 	 .compatible = "rockchip,rk3228-hdmi-phy",
140615081c50SWyon Bi 	 .data = (ulong)&inno_hdmi_phy_driver_data,
140715081c50SWyon Bi 	},
140819957ff8SAlgea Cao 	{
140919957ff8SAlgea Cao 	 .compatible = "rockchip,rk3528-hdmi-phy",
141019957ff8SAlgea Cao 	 .data = (ulong)&inno_hdmi_phy_driver_data,
141119957ff8SAlgea Cao 	},
141215081c50SWyon Bi 	{}
141315081c50SWyon Bi };
141415081c50SWyon Bi 
14156a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
inno_spl_hdmi_phy_probe(struct display_state * state)14166a3f4548SSandy Huang int inno_spl_hdmi_phy_probe(struct display_state *state)
14176a3f4548SSandy Huang {
14186a3f4548SSandy Huang 	struct inno_hdmi_phy *inno = malloc(sizeof(struct inno_hdmi_phy));
14196a3f4548SSandy Huang 
14206a3f4548SSandy Huang 	memset(inno, 0, sizeof(*inno));
14216a3f4548SSandy Huang 	g_inno = inno;
14226a3f4548SSandy Huang 
14236a3f4548SSandy Huang 	state->conn_state.connector->phy = &inno_hdmi_phy_driver_data;
14246a3f4548SSandy Huang 	state->conn_state.connector->phy->data = (void *)inno;
14256a3f4548SSandy Huang 	return 0;
14266a3f4548SSandy Huang }
14276a3f4548SSandy Huang #else
inno_hdmi_phy_probe(struct udevice * dev)142815081c50SWyon Bi static int inno_hdmi_phy_probe(struct udevice *dev)
142915081c50SWyon Bi {
143015081c50SWyon Bi 	struct inno_hdmi_phy *inno = dev_get_priv(dev);
143115081c50SWyon Bi 	struct rockchip_phy *phy =
143215081c50SWyon Bi 		(struct rockchip_phy *)dev_get_driver_data(dev);
143315081c50SWyon Bi 
143415081c50SWyon Bi 	inno->dev = dev;
143515081c50SWyon Bi 	phy->dev = dev;
143615081c50SWyon Bi 
143719957ff8SAlgea Cao 	g_inno = inno;
143819957ff8SAlgea Cao 	dev->driver_data = (ulong)&inno_hdmi_phy_driver_data;
143919957ff8SAlgea Cao 	phy = &inno_hdmi_phy_driver_data;
144019957ff8SAlgea Cao 
144119957ff8SAlgea Cao 	return 0;
144219957ff8SAlgea Cao }
14436a3f4548SSandy Huang #endif
144419957ff8SAlgea Cao 
rockchip_inno_phy_hdmi_bind(struct udevice * parent)144519957ff8SAlgea Cao static int rockchip_inno_phy_hdmi_bind(struct udevice *parent)
144619957ff8SAlgea Cao {
144719957ff8SAlgea Cao 	struct udevice *child;
144819957ff8SAlgea Cao 	ofnode subnode;
144919957ff8SAlgea Cao 	int ret;
145019957ff8SAlgea Cao 
145119957ff8SAlgea Cao 	subnode = ofnode_find_subnode(parent->node, "clk-port");
145219957ff8SAlgea Cao 	if (!ofnode_valid(subnode)) {
145380d7c6a5SJoseph Chen 		printf("%s: no subnode for %s\n", __func__, parent->name);
145419957ff8SAlgea Cao 		return -ENXIO;
145519957ff8SAlgea Cao 	}
145619957ff8SAlgea Cao 
145719957ff8SAlgea Cao 	ret = device_bind_driver_to_node(parent, "clk_inno_hdmi", "inno_hdmi_pll_clk", subnode, &child);
145819957ff8SAlgea Cao 	if (ret) {
145919957ff8SAlgea Cao 		printf("%s: clk-port cannot bind its driver\n", __func__);
146019957ff8SAlgea Cao 		return ret;
146119957ff8SAlgea Cao 	}
146219957ff8SAlgea Cao 
146315081c50SWyon Bi 	return 0;
146415081c50SWyon Bi }
146515081c50SWyon Bi 
146615081c50SWyon Bi U_BOOT_DRIVER(inno_hdmi_phy) = {
146715081c50SWyon Bi 	.name = "inno_hdmi_phy",
146815081c50SWyon Bi 	.id = UCLASS_PHY,
146915081c50SWyon Bi 	.of_match = inno_hdmi_phy_ids,
14706a3f4548SSandy Huang #ifndef CONFIG_SPL_BUILD
147115081c50SWyon Bi 	.probe = inno_hdmi_phy_probe,
14726a3f4548SSandy Huang #endif
147319957ff8SAlgea Cao 	.bind = rockchip_inno_phy_hdmi_bind,
147415081c50SWyon Bi 	.priv_auto_alloc_size = sizeof(struct inno_hdmi_phy),
147515081c50SWyon Bi };
147619957ff8SAlgea Cao 
147719957ff8SAlgea Cao 
inno_hdmi_clk_get_rate(struct clk * clk)147819957ff8SAlgea Cao static ulong inno_hdmi_clk_get_rate(struct clk *clk)
147919957ff8SAlgea Cao {
148019957ff8SAlgea Cao 	struct clk_inno_hdmi *priv = dev_get_priv(clk->dev);
148119957ff8SAlgea Cao 
148219957ff8SAlgea Cao 	return priv->rate;
148319957ff8SAlgea Cao }
148419957ff8SAlgea Cao 
inno_hdmi_clk_set_rate(struct clk * clk,ulong rate)148519957ff8SAlgea Cao static ulong inno_hdmi_clk_set_rate(struct clk *clk, ulong rate)
148619957ff8SAlgea Cao {
148719957ff8SAlgea Cao 	struct clk_inno_hdmi *priv = dev_get_priv(clk->dev);
148819957ff8SAlgea Cao 	int ret;
148919957ff8SAlgea Cao 
149019957ff8SAlgea Cao 	inno_hdmi_phy_clk_prepare(g_inno);
149119957ff8SAlgea Cao 	inno_hdmi_phy_clk_is_prepared(g_inno);
149219957ff8SAlgea Cao 	ret = inno_hdmi_phy_clk_set_rate(g_inno, rate);
149319957ff8SAlgea Cao 	if (ret < 0) {
149419957ff8SAlgea Cao 		printf("inno hdmi set rate failed ret:%d\n", ret);
149519957ff8SAlgea Cao 		return ret;
149619957ff8SAlgea Cao 	}
149719957ff8SAlgea Cao 
149819957ff8SAlgea Cao 	priv->rate = g_inno->pixclock;
149919957ff8SAlgea Cao 
150019957ff8SAlgea Cao 	return priv->rate;
150119957ff8SAlgea Cao }
150219957ff8SAlgea Cao 
150319957ff8SAlgea Cao static const struct clk_ops inno_hdmi_clk_ops = {
150419957ff8SAlgea Cao 	.get_rate	= inno_hdmi_clk_get_rate,
150519957ff8SAlgea Cao 	.set_rate	= inno_hdmi_clk_set_rate,
150619957ff8SAlgea Cao };
150719957ff8SAlgea Cao 
inno_hdmi_clk_probe(struct udevice * dev)150819957ff8SAlgea Cao static int inno_hdmi_clk_probe(struct udevice *dev)
150919957ff8SAlgea Cao {
151019957ff8SAlgea Cao 	return 0;
151119957ff8SAlgea Cao }
151219957ff8SAlgea Cao 
151319957ff8SAlgea Cao /*
151419957ff8SAlgea Cao  * In order for other display interfaces to use hdmiphy as source
151519957ff8SAlgea Cao  * for dclk, hdmiphy must register a virtual clock driver
151619957ff8SAlgea Cao  */
151719957ff8SAlgea Cao U_BOOT_DRIVER(clk_inno_hdmi) = {
151819957ff8SAlgea Cao 	.name		= "clk_inno_hdmi",
151919957ff8SAlgea Cao 	.id		= UCLASS_CLK,
152019957ff8SAlgea Cao 	.priv_auto_alloc_size = sizeof(struct clk_inno_hdmi),
152119957ff8SAlgea Cao 	.ops		= &inno_hdmi_clk_ops,
152219957ff8SAlgea Cao 	.probe		= inno_hdmi_clk_probe,
152319957ff8SAlgea Cao };
1524