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