diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 02de761f5fb9..63d22be23feb 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -133,6 +133,13 @@ config PHY_ROCKCHIP_SAMSUNG_DCPHY Enable this to support the Rockchip MIPI DCPHY with Samsung IP block. +config PHY_ROCKCHIP_SAMSUNG_HDPTX + tristate "Rockchip Samsung HDMI/DP Combo PHY driver" + depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) + select GENERIC_PHY + help + Support for Rockchip HDMI/DP Combo PHY with Samsung IP block. + config PHY_ROCKCHIP_SNPS_PCIE3 tristate "Rockchip Snps PCIe3 PHY Driver" depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index faa63cdbcf6d..82f0228d3839 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_NANENG_EDP) += phy-rockchip-naneng-edp.o obj-$(CONFIG_PHY_ROCKCHIP_NANENG_USB2) += phy-rockchip-naneng-usb2.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_DCPHY) += phy-rockchip-samsung-dcphy.o +obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c new file mode 100644 index 000000000000..28bbde5bfb85 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip HDMI/DP Combo PHY with Samsung IP block + * + * Copyright (c) 2021 Rockchip Electronics Co. Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HDPTXPHY_GRF_CON0 0x0000 +#define RO_REF_CLK_SEL GENMASK(11, 10) +#define LC_REF_CLK_SEL GENMASK(9, 8) +#define PLL_EN BIT(7) +#define BIAS_EN BIT(6) +#define BGR_EN BIT(5) +#define HDPTX_MODE_SEL BIT(0) +#define HDPTXPHY_GRF_STATUS0 0x0080 +#define PLL_LOCK_DONE BIT(3) +#define PHY_CLK_RDY BIT(2) +#define PHY_RDY BIT(1) +#define SB_RDY BIT(0) + +/* cmn_reg0008 */ +#define OVRD_LCPLL_EN BIT(7) +#define LCPLL_EN BIT(6) + +/* cmn_reg003C */ +#define ANA_LCPLL_RESERVED7 BIT(7) + +/* cmn_reg003D */ +#define OVRD_ROPLL_EN BIT(7) +#define ROPLL_EN BIT(6) + +/* cmn_reg0046 */ +#define ROPLL_ANA_CPP_CTRL_COARSE GENMASK(7, 4) +#define ROPLL_ANA_CPP_CTRL_FINE GENMASK(3, 0) + +/* cmn_reg0047 */ +#define ROPLL_ANA_LPF_C_SEL_COARSE GENMASK(5, 3) +#define ROPLL_ANA_LPF_C_SEL_FINE GENMASK(2, 0) + +/* cmn_reg004E */ +#define ANA_ROPLL_PI_EN BIT(5) + +/* cmn_reg0051 */ +#define ROPLL_PMS_MDIV GENMASK(7, 0) + +/* cmn_reg0055 */ +#define ROPLL_PMS_MDIV_AFC GENMASK(7, 0) + +/* cmn_reg0059 */ +#define ANA_ROPLL_PMS_PDIV GENMASK(7, 4) +#define ANA_ROPLL_PMS_REFDIV GENMASK(3, 0) + +/* cmn_reg005A */ +#define ROPLL_PMS_SDIV_RBR GENMASK(7, 4) +#define ROPLL_PMS_SDIV_HBR GENMASK(3, 0) + +/* cmn_reg005B */ +#define ROPLL_PMS_SDIV_HBR2 GENMASK(7, 4) +#define ROPLL_PMS_SDIV_HBR3 GENMASK(3, 0) + +/* cmn_reg005D */ +#define OVRD_ROPLL_REF_CLK_SEL BIT(5) +#define ROPLL_REF_CLK_SEL GENMASK(4, 3) + +/* cmn_reg005E */ +#define ANA_ROPLL_SDM_EN BIT(6) +#define OVRD_ROPLL_SDM_RSTN BIT(5) +#define ROPLL_SDM_RSTN BIT(4) +#define ROPLL_SDC_FRACTIONAL_EN_RBR BIT(3) +#define ROPLL_SDC_FRACTIONAL_EN_HBR BIT(2) +#define ROPLL_SDC_FRACTIONAL_EN_HBR2 BIT(1) +#define ROPLL_SDC_FRACTIONAL_EN_HBR3 BIT(0) + +/* cmn_reg005F */ +#define OVRD_ROPLL_SDC_RSTN BIT(5) +#define ROPLL_SDC_RSTN BIT(4) + +/* cmn_reg0060 */ +#define ROPLL_SDM_DENOMINATOR GENMASK(7, 0) + +/* cmn_reg0064 */ +#define ROPLL_SDM_NUMERATOR_SIGN_RBR BIT(3) +#define ROPLL_SDM_NUMERATOR_SIGN_HBR BIT(2) +#define ROPLL_SDM_NUMERATOR_SIGN_HBR2 BIT(1) +#define ROPLL_SDM_NUMERATOR_SIGN_HBR3 BIT(0) + +/* cmn_reg0065 */ +#define ROPLL_SDM_NUMERATOR GENMASK(7, 0) + +/* cmn_reg0069 */ +#define ROPLL_SDC_N_RBR GENMASK(2, 0) + +/* cmn_reg006A */ +#define ROPLL_SDC_N_HBR GENMASK(5, 3) +#define ROPLL_SDC_N_HBR2 GENMASK(2, 0) + +/* cmn_reg006B */ +#define ROPLL_SDC_N_HBR3 GENMASK(3, 1) + +/* cmn_reg006C */ +#define ROPLL_SDC_NUMERATOR GENMASK(5, 0) + +/* cmn_reg0070 */ +#define ROPLL_SDC_DENOMINATOR GENMASK(5, 0) + +/* cmn_reg0074 */ +#define OVRD_ROPLL_SDC_NDIV_RSTN BIT(3) +#define ROPLL_SDC_NDIV_RSTN BIT(2) +#define OVRD_ROPLL_SSC_EN BIT(1) +#define ROPLL_SSC_EN BIT(0) + +/* cmn_reg0075 */ +#define ANA_ROPLL_SSC_FM_DEVIATION GENMASK(5, 0) + +/* cmn_reg0076 */ +#define ANA_ROPLL_SSC_FM_FREQ GENMASK(6, 2) + +/* cmn_reg0077 */ +#define ANA_ROPLL_SSC_CLK_DIV_SEL GENMASK(6, 3) + +/* cmn_reg0081 */ +#define ANA_PLL_CD_TX_SER_RATE_SEL BIT(3) +#define ANA_PLL_CD_HSCLK_WEST_EN BIT(1) +#define ANA_PLL_CD_HSCLK_EAST_EN BIT(0) + +/* cmn_reg0082 */ +#define ANA_PLL_CD_VREG_GAIN_CTRL GENMASK(3, 0) + +/* cmn_reg0083 */ +#define ANA_PLL_CD_VREG_ICTRL GENMASK(6, 5) + +/* cmn_reg0084 */ +#define PLL_LCRO_CLK_SEL BIT(5) + +/* cmn_reg0085 */ +#define ANA_PLL_SYNC_LOSS_DET_MODE GENMASK(1, 0) + +/* cmn_reg0087 */ +#define ANA_PLL_TX_HS_CLK_EN BIT(2) + +/* cmn_reg0095 */ +#define DP_TX_LINK_BW GENMASK(1, 0) + +/* cmn_reg0097 */ +#define DIG_CLK_SEL BIT(1) + +/* cmn_reg0099 */ +#define SSC_EN GENMASK(7, 6) +#define CMN_ROPLL_ALONE_MODE BIT(2) + +/* cmn_reg009A */ +#define HS_SPEED_SEL BIT(0) + +/* cmn_reg009B */ +#define LS_SPEED_SEL BIT(4) + +/* sb_reg0102 */ +#define OVRD_SB_RXTERM_EN BIT(5) +#define SB_RXRERM_EN BIT(4) +#define ANA_SB_RXTERM_OFFSP GENMASK(3, 0) + +/* sb_reg0103 */ +#define ANA_SB_RXTERM_OFFSN GENMASK(6, 3) +#define OVRD_SB_RX_RESCAL_DONE BIT(1) +#define SB_RX_RESCAL_DONE BIT(0) + +/* sb_reg0104 */ +#define OVRD_SB_EN BIT(5) +#define SB_EN BIT(4) +#define OVRD_SB_AUX_EN BIT(1) +#define SB_AUX_EN BIT(0) + +/* sb_reg010D */ +#define ANA_SB_DMRX_LPBK_DATA BIT(4) + +/* sb_reg010F */ +#define OVRD_SB_VREG_EN BIT(7) +#define ANA_SB_VREG_GAIN_CTRL GENMASK(3, 0) + +/* sb_reg0110 */ +#define ANA_SB_VREG_OUT_SEL BIT(1) +#define ANA_SB_VREG_REF_SEL BIT(0) + +/* sb_reg0113 */ +#define SB_VREG_EN BIT(6) +#define SB_RX_RCAL_OPT_CODE GENMASK(5, 4) +#define SB_RX_RTERM_CTRL GENMASK(3, 0) + +/* sb_reg0114 */ +#define SB_TG_SB_EN_DELAY_TIME GENMASK(5, 3) +#define SB_TG_RXTERN_EN_DELAY_TIME GENMASK(2, 0) + +/* sb_reg0115 */ +#define SB_READY_DELAY_TIME GENMASK(5, 3) +#define SB_TG_OSC_EN_DELAY_TIME GENMASK(2, 0) + +/* sb_reg0116 */ +#define SB_TG_OSC_EN_TO_AFC_RSTN_DELAT_TIME GENMASK(6, 4) + +/* sb_reg0117 */ +#define SB_TG_PLL_CD_VREG_FAST_PULSE_TIME GENMASK(3, 0) + +/* sb_reg0118 */ +#define SB_TG_EARC_DMRX_RECVRD_CLK_CNT GENMASK(7, 0) + +/* sb_reg011A */ +#define SB_TG_CNT_RUN_NO_7_0 GENMASK(7, 0) + +/* sb_reg011B */ +#define SB_EARC_SIG_DET_BYPASS BIT(4) +#define SB_AFC_TOL GENMASK(3, 0) + +/* sb_reg011C */ +#define SB_AFC_STB_NUM GENMASK(3, 0) + +/* sb_reg011D */ +#define SB_TG_OSC_CNT_MIN GENMASK(7, 0) + +/* sb_reg011E */ +#define SB_TG_OSC_CNT_MAX GENMASK(7, 0) + +/* sb_reg011F */ +#define SB_PWM_AFC_CTRL GENMASK(7, 2) +#define SB_RCAL_RSTN BIT(1) + +/* sb_reg0120 */ +#define SB_AUX_EN_IN BIT(7) + +/* sb_reg0123 */ +#define OVRD_SB_READY BIT(5) +#define SB_READY BIT(4) + +/* lntop_reg0200 */ +#define PROTOCOL_SEL BIT(2) + +/* lntop_reg0206 */ +#define DATA_BUS_WIDTH GENMASK(2, 1) +#define BUS_WIDTH_SEL BIT(0) + +/* lntop_reg0207 */ +#define LANE_EN GENMASK(3, 0) + +/* lane_reg0303 */ +#define OVRD_LN_TX_DRV_LVL_CTRL BIT(5) +#define LN_TX_DRV_LVL_CTRL GENMASK(4, 0) + +/* lane_reg0304 */ +#define OVRD_LN_TX_DRV_POST_LVL_CTRL BIT(4) +#define LN_TX_DRV_POST_LVL_CTRL GENMASK(3, 0) + +/* lane_reg0305 */ +#define OVRD_LN_TX_DRV_PRE_LVL_CTRL BIT(6) +#define LN_TX_DRV_PRE_LVL_CTRL GENMASK(5, 2) + +/* lane_reg030A */ +#define LN_ANA_TX_JEQ_EN BIT(4) +#define LN_TX_JEQ_EVEN_CTRL_RBR GENMASK(3, 0) + +/* lane_reg030B */ +#define LN_TX_JEQ_EVEN_CTRL_HBR GENMASK(7, 4) +#define LN_TX_JEQ_EVEN_CTRL_HBR2 GENMASK(3, 0) + +/* lane_reg030C */ +#define LN_TX_JEQ_EVEN_CTRL_HBR3 GENMASK(7, 4) +#define LN_TX_JEQ_ODD_CTRL_RBR GENMASK(3, 0) + +/* lane_reg030D */ +#define LN_TX_JEQ_ODD_CTRL_HBR GENMASK(7, 4) +#define LN_TX_JEQ_ODD_CTRL_HBR2 GENMASK(3, 0) + +/* lane_reg030E */ +#define LN_TX_JEQ_ODD_CTRL_HBR3 GENMASK(7, 4) + +/* lane_reg0307 */ +#define LN_ANA_TX_DRV_ACCDRV_POL_SEL BIT(6) +#define LN_ANA_TX_DRV_ACCDRV_CTRL GENMASK(5, 3) + +/* lane_reg0310 */ +#define LN_ANA_TX_SYNC_LOSS_DET_MODE GENMASK(1, 0) + +/* lane_reg0311 */ +#define LN_TX_SER_40BIT_EN_RBR BIT(3) +#define LN_TX_SER_40BIT_EN_HBR BIT(2) +#define LN_TX_SER_40BIT_EN_HBR2 BIT(1) +#define LN_TX_SER_40BIT_EN_HBR3 BIT(0) + +/* lane_reg0316 */ +#define LN_ANA_TX_SER_VREG_GAIN_CTRL GENMASK(3, 0) + +/* lane_reg031B */ +#define LN_ANA_TX_RESERVED GENMASK(7, 0) + +/* lane_reg031E */ +#define LN_POLARITY_INV BIT(2) + +#define LANE_REG(lane, offset) (0x400 * (lane) + (offset)) + +struct rockchip_hdptx_phy { + struct device *dev; + struct clk_bulk_data *clks; + int nr_clks; + struct reset_control *apb_reset; + struct reset_control *cmn_reset; + struct reset_control *init_reset; + struct reset_control *lane_reset; + struct regmap *regmap; + struct regmap *grf; + u32 lane_polarity_invert[4]; +}; + +enum { + DP_BW_RBR, + DP_BW_HBR, + DP_BW_HBR2, + DP_BW_HBR3, +}; + +static struct { + u8 tx_amp; + u8 tx_de_emp; + u8 tx_pre_emp; +} training_table[4][4] = { + /* voltage swing 0, pre-emphasis 0->3 */ + { + { .tx_amp = 0x3, .tx_de_emp = 0x1, .tx_pre_emp = 0x1 }, + { .tx_amp = 0x9, .tx_de_emp = 0x7, .tx_pre_emp = 0x0 }, + { .tx_amp = 0xc, .tx_de_emp = 0xa, .tx_pre_emp = 0x0 }, + { .tx_amp = 0xd, .tx_de_emp = 0xc, .tx_pre_emp = 0x0 } + }, + + /* voltage swing 1, pre-emphasis 0->2 */ + { + { .tx_amp = 0x6, .tx_de_emp = 0x1, .tx_pre_emp = 0x1 }, + { .tx_amp = 0xc, .tx_de_emp = 0x7, .tx_pre_emp = 0x0 }, + { .tx_amp = 0xd, .tx_de_emp = 0x9, .tx_pre_emp = 0x0 }, + }, + + /* voltage swing 2, pre-emphasis 0->1 */ + { + { .tx_amp = 0x9, .tx_de_emp = 0x1, .tx_pre_emp = 0x1 }, + { .tx_amp = 0xd, .tx_de_emp = 0x6, .tx_pre_emp = 0x0 }, + }, + + /* voltage swing 3, pre-emphasis 0 */ + { + { .tx_amp = 0xd, .tx_de_emp = 0x1, .tx_pre_emp = 0x1 }, + } +}; + +static int rockchip_grf_write(struct regmap *grf, unsigned int reg, + unsigned int mask, unsigned int val) +{ + return regmap_write(grf, reg, (mask << 16) | (val & mask)); +} + +static int rockchip_hdptx_phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + return 0; +} + +static int rockchip_hdptx_phy_verify_config(struct rockchip_hdptx_phy *hdptx, + struct phy_configure_opts_dp *dp) +{ + int i; + + if (dp->set_rate) { + switch (dp->link_rate) { + case 1620: + case 2700: + case 5400: + break; + default: + return -EINVAL; + } + } + + switch (dp->lanes) { + case 1: + case 2: + case 4: + break; + default: + return -EINVAL; + } + + if (dp->set_voltages) { + for (i = 0; i < dp->lanes; i++) { + if (dp->voltage[i] > 3 || dp->pre[i] > 3) + return -EINVAL; + + if (dp->voltage[i] + dp->pre[i] > 3) + return -EINVAL; + } + } + + return 0; +} + +static void rockchip_hdptx_phy_set_voltage(struct rockchip_hdptx_phy *hdptx, + struct phy_configure_opts_dp *dp, + u8 lane) +{ + u32 val; + + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c28), + LN_ANA_TX_JEQ_EN, + FIELD_PREP(LN_ANA_TX_JEQ_EN, 0x1)); + + switch (dp->link_rate) { + case 1620: + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c28), + LN_TX_JEQ_EVEN_CTRL_RBR, + FIELD_PREP(LN_TX_JEQ_EVEN_CTRL_RBR, 0x7)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c30), + LN_TX_JEQ_ODD_CTRL_RBR, + FIELD_PREP(LN_TX_JEQ_ODD_CTRL_RBR, 0x7)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c44), + LN_TX_SER_40BIT_EN_RBR, + FIELD_PREP(LN_TX_SER_40BIT_EN_RBR, 0x1)); + break; + case 2700: + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c2c), + LN_TX_JEQ_EVEN_CTRL_HBR, + FIELD_PREP(LN_TX_JEQ_EVEN_CTRL_HBR, 0x7)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c34), + LN_TX_JEQ_ODD_CTRL_HBR, + FIELD_PREP(LN_TX_JEQ_ODD_CTRL_HBR, 0x7)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c44), + LN_TX_SER_40BIT_EN_HBR, + FIELD_PREP(LN_TX_SER_40BIT_EN_HBR, 0x1)); + break; + case 5400: + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c2c), + LN_TX_JEQ_EVEN_CTRL_HBR2, + FIELD_PREP(LN_TX_JEQ_EVEN_CTRL_HBR2, 0x7)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c34), + LN_TX_JEQ_ODD_CTRL_HBR2, + FIELD_PREP(LN_TX_JEQ_ODD_CTRL_HBR2, 0x7)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c44), + LN_TX_SER_40BIT_EN_HBR2, + FIELD_PREP(LN_TX_SER_40BIT_EN_HBR2, 0x1)); + break; + } + + val = training_table[dp->voltage[lane]][dp->pre[lane]].tx_amp; + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c0c), + OVRD_LN_TX_DRV_LVL_CTRL | LN_TX_DRV_LVL_CTRL, + FIELD_PREP(OVRD_LN_TX_DRV_LVL_CTRL, 0x1) | + FIELD_PREP(LN_TX_DRV_LVL_CTRL, val)); + + val = training_table[dp->voltage[lane]][dp->pre[lane]].tx_de_emp; + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c10), + OVRD_LN_TX_DRV_POST_LVL_CTRL | + LN_TX_DRV_POST_LVL_CTRL, + FIELD_PREP(OVRD_LN_TX_DRV_POST_LVL_CTRL, 0x1) | + FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL, val)); + + val = training_table[dp->voltage[lane]][dp->pre[lane]].tx_pre_emp; + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c14), + OVRD_LN_TX_DRV_PRE_LVL_CTRL | + LN_TX_DRV_PRE_LVL_CTRL, + FIELD_PREP(OVRD_LN_TX_DRV_PRE_LVL_CTRL, 0x1) | + FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL, val)); + + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c1c), + LN_ANA_TX_DRV_ACCDRV_POL_SEL | + LN_ANA_TX_DRV_ACCDRV_CTRL, + FIELD_PREP(LN_ANA_TX_DRV_ACCDRV_POL_SEL, 0x1) | + FIELD_PREP(LN_ANA_TX_DRV_ACCDRV_CTRL, 0x4)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c6c), + LN_ANA_TX_RESERVED, + FIELD_PREP(LN_ANA_TX_RESERVED, 0x1)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c58), + LN_ANA_TX_SER_VREG_GAIN_CTRL, + FIELD_PREP(LN_ANA_TX_SER_VREG_GAIN_CTRL, 0x2)); + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c40), + LN_ANA_TX_SYNC_LOSS_DET_MODE, + FIELD_PREP(LN_ANA_TX_SYNC_LOSS_DET_MODE, 0x3)); +} + +static int rockchip_hdptx_phy_set_voltages(struct rockchip_hdptx_phy *hdptx, + struct phy_configure_opts_dp *dp) +{ + u8 lane; + + for (lane = 0; lane < dp->lanes; lane++) + rockchip_hdptx_phy_set_voltage(hdptx, dp, lane); + + return 0; +} + +static int rockchip_hdptx_phy_set_rate(struct rockchip_hdptx_phy *hdptx, + struct phy_configure_opts_dp *dp) +{ + u32 bw, status; + int ret; + + reset_control_assert(hdptx->lane_reset); + udelay(20); + reset_control_assert(hdptx->cmn_reset); + udelay(20); + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, PLL_EN, + FIELD_PREP(PLL_EN, 0x0)); + udelay(20); + regmap_update_bits(hdptx->regmap, 0x081c, LANE_EN, + FIELD_PREP(LANE_EN, 0x0)); + + switch (dp->link_rate) { + case 1620: + bw = DP_BW_RBR; + break; + case 2700: + bw = DP_BW_HBR; + break; + case 5400: + bw = DP_BW_HBR2; + break; + default: + return -EINVAL; + } + + regmap_update_bits(hdptx->regmap, 0x0254, DP_TX_LINK_BW, + FIELD_PREP(DP_TX_LINK_BW, bw)); + + if (dp->ssc) { + regmap_update_bits(hdptx->regmap, 0x01d0, + OVRD_ROPLL_SSC_EN | ROPLL_SSC_EN, + FIELD_PREP(OVRD_ROPLL_SSC_EN, 0x1) | + FIELD_PREP(ROPLL_SSC_EN, 0x1)); + regmap_write(hdptx->regmap, 0x01d4, + FIELD_PREP(ANA_ROPLL_SSC_FM_DEVIATION, 0xe)); + regmap_update_bits(hdptx->regmap, 0x01d8, + ANA_ROPLL_SSC_FM_FREQ, + FIELD_PREP(ANA_ROPLL_SSC_FM_FREQ, 0x1a)); + regmap_update_bits(hdptx->regmap, 0x0264, SSC_EN, + FIELD_PREP(SSC_EN, 0x2)); + } else { + regmap_update_bits(hdptx->regmap, 0x01d0, + OVRD_ROPLL_SSC_EN | ROPLL_SSC_EN, + FIELD_PREP(OVRD_ROPLL_SSC_EN, 0x1) | + FIELD_PREP(ROPLL_SSC_EN, 0x0)); + regmap_write(hdptx->regmap, 0x01d4, + FIELD_PREP(ANA_ROPLL_SSC_FM_DEVIATION, 0x20)); + regmap_update_bits(hdptx->regmap, 0x01d8, + ANA_ROPLL_SSC_FM_FREQ, + FIELD_PREP(ANA_ROPLL_SSC_FM_FREQ, 0xc)); + regmap_update_bits(hdptx->regmap, 0x0264, SSC_EN, + FIELD_PREP(SSC_EN, 0x0)); + } + + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, PLL_EN, + FIELD_PREP(PLL_EN, 0x1)); + udelay(20); + reset_control_deassert(hdptx->cmn_reset); + udelay(20); + + ret = regmap_read_poll_timeout(hdptx->grf, HDPTXPHY_GRF_STATUS0, + status, FIELD_GET(PLL_LOCK_DONE, status), + 50, 1000); + if (ret) { + dev_err(hdptx->dev, "timeout waiting for pll_lock_done\n"); + return ret; + } + + regmap_update_bits(hdptx->regmap, 0x081c, LANE_EN, + FIELD_PREP(LANE_EN, GENMASK(dp->lanes - 1, 0))); + + reset_control_deassert(hdptx->lane_reset); + udelay(20); + + ret = regmap_read_poll_timeout(hdptx->grf, HDPTXPHY_GRF_STATUS0, + status, FIELD_PREP(PHY_RDY, status), + 50, 1000); + if (ret) { + dev_err(hdptx->dev, "timeout waiting for phy_rdy\n"); + return ret; + } + + return 0; +} + +static int rockchip_hdptx_phy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); + enum phy_mode mode = phy_get_mode(phy); + int ret; + + if (mode != PHY_MODE_DP) + return -EINVAL; + + ret = rockchip_hdptx_phy_verify_config(hdptx, &opts->dp); + if (ret) { + dev_err(hdptx->dev, "invalid params for phy configure\n"); + return ret; + } + + if (opts->dp.set_rate) { + ret = rockchip_hdptx_phy_set_rate(hdptx, &opts->dp); + if (ret) { + dev_err(hdptx->dev, "failed to set rate: %d\n", ret); + return ret; + } + } + + if (opts->dp.set_voltages) { + ret = rockchip_hdptx_phy_set_voltages(hdptx, &opts->dp); + if (ret) { + dev_err(hdptx->dev, "failed to set voltages: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void rockchip_hdptx_phy_dp_pll_init(struct rockchip_hdptx_phy *hdptx) +{ + regmap_update_bits(hdptx->regmap, 0x0020, OVRD_LCPLL_EN | LCPLL_EN, + FIELD_PREP(OVRD_LCPLL_EN, 0x1) | + FIELD_PREP(LCPLL_EN, 0x0)); + regmap_update_bits(hdptx->regmap, 0x00f4, OVRD_ROPLL_EN | ROPLL_EN, + FIELD_PREP(OVRD_ROPLL_EN, 0x1) | + FIELD_PREP(ROPLL_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0138, ANA_ROPLL_PI_EN, + FIELD_PREP(ANA_ROPLL_PI_EN, 0x1)); + + regmap_write(hdptx->regmap, 0x0144, FIELD_PREP(ROPLL_PMS_MDIV, 0x87)); + regmap_write(hdptx->regmap, 0x0148, FIELD_PREP(ROPLL_PMS_MDIV, 0x71)); + regmap_write(hdptx->regmap, 0x014c, FIELD_PREP(ROPLL_PMS_MDIV, 0x71)); + + regmap_write(hdptx->regmap, 0x0154, + FIELD_PREP(ROPLL_PMS_MDIV_AFC, 0x87)); + regmap_write(hdptx->regmap, 0x0158, + FIELD_PREP(ROPLL_PMS_MDIV_AFC, 0x71)); + regmap_write(hdptx->regmap, 0x015c, + FIELD_PREP(ROPLL_PMS_MDIV_AFC, 0x71)); + + regmap_write(hdptx->regmap, 0x0164, + FIELD_PREP(ANA_ROPLL_PMS_PDIV, 0x1) | + FIELD_PREP(ANA_ROPLL_PMS_REFDIV, 0x1)); + + regmap_write(hdptx->regmap, 0x0168, + FIELD_PREP(ROPLL_PMS_SDIV_RBR, 0x3) | + FIELD_PREP(ROPLL_PMS_SDIV_HBR, 0x1)); + regmap_update_bits(hdptx->regmap, 0x016c, ROPLL_PMS_SDIV_HBR2, + FIELD_PREP(ROPLL_PMS_SDIV_HBR2, 0x0)); + + regmap_update_bits(hdptx->regmap, 0x0178, ANA_ROPLL_SDM_EN, + FIELD_PREP(ANA_ROPLL_SDM_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0178, + OVRD_ROPLL_SDM_RSTN | ROPLL_SDM_RSTN, + FIELD_PREP(OVRD_ROPLL_SDM_RSTN, 0x1) | + FIELD_PREP(ROPLL_SDM_RSTN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0178, ROPLL_SDC_FRACTIONAL_EN_RBR, + FIELD_PREP(ROPLL_SDC_FRACTIONAL_EN_RBR, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0178, ROPLL_SDC_FRACTIONAL_EN_HBR, + FIELD_PREP(ROPLL_SDC_FRACTIONAL_EN_HBR, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0178, ROPLL_SDC_FRACTIONAL_EN_HBR2, + FIELD_PREP(ROPLL_SDC_FRACTIONAL_EN_HBR2, 0x1)); + regmap_update_bits(hdptx->regmap, 0x017c, + OVRD_ROPLL_SDC_RSTN | ROPLL_SDC_RSTN, + FIELD_PREP(OVRD_ROPLL_SDC_RSTN, 0x1) | + FIELD_PREP(ROPLL_SDC_RSTN, 0x1)); + + regmap_write(hdptx->regmap, 0x0180, + FIELD_PREP(ROPLL_SDM_DENOMINATOR, 0x21)); + regmap_write(hdptx->regmap, 0x0184, + FIELD_PREP(ROPLL_SDM_DENOMINATOR, 0x27)); + regmap_write(hdptx->regmap, 0x0188, + FIELD_PREP(ROPLL_SDM_DENOMINATOR, 0x27)); + + regmap_update_bits(hdptx->regmap, 0x0190, + ROPLL_SDM_NUMERATOR_SIGN_RBR | + ROPLL_SDM_NUMERATOR_SIGN_HBR | + ROPLL_SDM_NUMERATOR_SIGN_HBR2, + FIELD_PREP(ROPLL_SDM_NUMERATOR_SIGN_RBR, 0x0) | + FIELD_PREP(ROPLL_SDM_NUMERATOR_SIGN_HBR, 0x1) | + FIELD_PREP(ROPLL_SDM_NUMERATOR_SIGN_HBR2, 0x1)); + + regmap_write(hdptx->regmap, 0x0194, + FIELD_PREP(ROPLL_SDM_NUMERATOR, 0x0)); + regmap_write(hdptx->regmap, 0x0198, + FIELD_PREP(ROPLL_SDM_NUMERATOR, 0xd)); + regmap_write(hdptx->regmap, 0x019c, + FIELD_PREP(ROPLL_SDM_NUMERATOR, 0xd)); + + regmap_update_bits(hdptx->regmap, 0x01a4, ROPLL_SDC_N_RBR, + FIELD_PREP(ROPLL_SDC_N_RBR, 0x2)); + regmap_update_bits(hdptx->regmap, 0x01a8, + ROPLL_SDC_N_HBR | ROPLL_SDC_N_HBR2, + FIELD_PREP(ROPLL_SDC_N_HBR, 0x1) | + FIELD_PREP(ROPLL_SDC_N_HBR2, 0x1)); + + regmap_write(hdptx->regmap, 0x01b0, + FIELD_PREP(ROPLL_SDC_NUMERATOR, 0x3)); + regmap_write(hdptx->regmap, 0x01b4, + FIELD_PREP(ROPLL_SDC_NUMERATOR, 0x7)); + regmap_write(hdptx->regmap, 0x01b8, + FIELD_PREP(ROPLL_SDC_NUMERATOR, 0x7)); + + regmap_write(hdptx->regmap, 0x01c0, + FIELD_PREP(ROPLL_SDC_DENOMINATOR, 0x8)); + regmap_write(hdptx->regmap, 0x01c4, + FIELD_PREP(ROPLL_SDC_DENOMINATOR, 0x18)); + regmap_write(hdptx->regmap, 0x01c8, + FIELD_PREP(ROPLL_SDC_DENOMINATOR, 0x18)); + + regmap_update_bits(hdptx->regmap, 0x01d0, + OVRD_ROPLL_SDC_NDIV_RSTN | ROPLL_SDC_NDIV_RSTN, + FIELD_PREP(OVRD_ROPLL_SDC_NDIV_RSTN, 0x1) | + FIELD_PREP(ROPLL_SDC_NDIV_RSTN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x01dc, ANA_ROPLL_SSC_CLK_DIV_SEL, + FIELD_PREP(ANA_ROPLL_SSC_CLK_DIV_SEL, 0x1)); + + regmap_update_bits(hdptx->regmap, 0x0118, + ROPLL_ANA_CPP_CTRL_COARSE | ROPLL_ANA_CPP_CTRL_FINE, + FIELD_PREP(ROPLL_ANA_CPP_CTRL_COARSE, 0xe) | + FIELD_PREP(ROPLL_ANA_CPP_CTRL_FINE, 0xe)); + regmap_update_bits(hdptx->regmap, 0x011c, + ROPLL_ANA_LPF_C_SEL_COARSE | + ROPLL_ANA_LPF_C_SEL_FINE, + FIELD_PREP(ROPLL_ANA_LPF_C_SEL_COARSE, 0x4) | + FIELD_PREP(ROPLL_ANA_LPF_C_SEL_FINE, 0x4)); + + regmap_update_bits(hdptx->regmap, 0x0204, ANA_PLL_CD_TX_SER_RATE_SEL, + FIELD_PREP(ANA_PLL_CD_TX_SER_RATE_SEL, 0x0)); + + regmap_update_bits(hdptx->regmap, 0x025c, DIG_CLK_SEL, + FIELD_PREP(DIG_CLK_SEL, 0x1)); + regmap_update_bits(hdptx->regmap, 0x021c, ANA_PLL_TX_HS_CLK_EN, + FIELD_PREP(ANA_PLL_TX_HS_CLK_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0204, + ANA_PLL_CD_HSCLK_EAST_EN | ANA_PLL_CD_HSCLK_WEST_EN, + FIELD_PREP(ANA_PLL_CD_HSCLK_EAST_EN, 0x1) | + FIELD_PREP(ANA_PLL_CD_HSCLK_WEST_EN, 0x0)); + regmap_update_bits(hdptx->regmap, 0x0264, CMN_ROPLL_ALONE_MODE, + FIELD_PREP(CMN_ROPLL_ALONE_MODE, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0208, ANA_PLL_CD_VREG_GAIN_CTRL, + FIELD_PREP(ANA_PLL_CD_VREG_GAIN_CTRL, 0x4)); + regmap_update_bits(hdptx->regmap, 0x00f0, ANA_LCPLL_RESERVED7, + FIELD_PREP(ANA_LCPLL_RESERVED7, 0x1)); + regmap_update_bits(hdptx->regmap, 0x020c, ANA_PLL_CD_VREG_ICTRL, + FIELD_PREP(ANA_PLL_CD_VREG_ICTRL, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0214, ANA_PLL_SYNC_LOSS_DET_MODE, + FIELD_PREP(ANA_PLL_SYNC_LOSS_DET_MODE, 0x3)); + regmap_update_bits(hdptx->regmap, 0x0210, PLL_LCRO_CLK_SEL, + FIELD_PREP(PLL_LCRO_CLK_SEL, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0268, HS_SPEED_SEL, + FIELD_PREP(HS_SPEED_SEL, 0x1)); + regmap_update_bits(hdptx->regmap, 0x026c, LS_SPEED_SEL, + FIELD_PREP(LS_SPEED_SEL, 0x1)); +} + +static int rockchip_hdptx_phy_dp_aux_init(struct rockchip_hdptx_phy *hdptx) +{ + u32 status; + int ret; + + regmap_update_bits(hdptx->regmap, 0x044c, + SB_RX_RCAL_OPT_CODE | SB_RX_RTERM_CTRL, + FIELD_PREP(SB_RX_RCAL_OPT_CODE, 0x1) | + FIELD_PREP(SB_RX_RTERM_CTRL, 0x3)); + regmap_update_bits(hdptx->regmap, 0x0450, + SB_TG_SB_EN_DELAY_TIME | SB_TG_RXTERN_EN_DELAY_TIME, + FIELD_PREP(SB_TG_SB_EN_DELAY_TIME, 0x2) | + FIELD_PREP(SB_TG_RXTERN_EN_DELAY_TIME, 0x2)); + regmap_update_bits(hdptx->regmap, 0x0454, + SB_READY_DELAY_TIME | SB_TG_OSC_EN_DELAY_TIME, + FIELD_PREP(SB_READY_DELAY_TIME, 0x2) | + FIELD_PREP(SB_TG_OSC_EN_DELAY_TIME, 0x2)); + regmap_update_bits(hdptx->regmap, 0x0458, + SB_TG_OSC_EN_TO_AFC_RSTN_DELAT_TIME, + FIELD_PREP(SB_TG_OSC_EN_TO_AFC_RSTN_DELAT_TIME, 0x2)); + regmap_update_bits(hdptx->regmap, 0x045c, + SB_TG_PLL_CD_VREG_FAST_PULSE_TIME, + FIELD_PREP(SB_TG_PLL_CD_VREG_FAST_PULSE_TIME, 0x4)); + regmap_update_bits(hdptx->regmap, 0x0460, + SB_TG_EARC_DMRX_RECVRD_CLK_CNT, + FIELD_PREP(SB_TG_EARC_DMRX_RECVRD_CLK_CNT, 0xa)); + regmap_update_bits(hdptx->regmap, 0x0468, SB_TG_CNT_RUN_NO_7_0, + FIELD_PREP(SB_TG_CNT_RUN_NO_7_0, 0x3)); + regmap_update_bits(hdptx->regmap, 0x046c, + SB_EARC_SIG_DET_BYPASS | SB_AFC_TOL, + FIELD_PREP(SB_EARC_SIG_DET_BYPASS, 0x1) | + FIELD_PREP(SB_AFC_TOL, 0x3)); + regmap_update_bits(hdptx->regmap, 0x0470, SB_AFC_STB_NUM, + FIELD_PREP(SB_AFC_STB_NUM, 0x4)); + regmap_update_bits(hdptx->regmap, 0x0474, SB_TG_OSC_CNT_MIN, + FIELD_PREP(SB_TG_OSC_CNT_MIN, 0x67)); + regmap_update_bits(hdptx->regmap, 0x0478, SB_TG_OSC_CNT_MAX, + FIELD_PREP(SB_TG_OSC_CNT_MAX, 0x6a)); + regmap_update_bits(hdptx->regmap, 0x047c, SB_PWM_AFC_CTRL, + FIELD_PREP(SB_PWM_AFC_CTRL, 0x5)); + regmap_update_bits(hdptx->regmap, 0x0434, ANA_SB_DMRX_LPBK_DATA, + FIELD_PREP(ANA_SB_DMRX_LPBK_DATA, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0440, + ANA_SB_VREG_OUT_SEL | ANA_SB_VREG_REF_SEL, + FIELD_PREP(ANA_SB_VREG_OUT_SEL, 0x1) | + FIELD_PREP(ANA_SB_VREG_REF_SEL, 0x1)); + regmap_update_bits(hdptx->regmap, 0x043c, ANA_SB_VREG_GAIN_CTRL, + FIELD_PREP(ANA_SB_VREG_GAIN_CTRL, 0x0)); + regmap_update_bits(hdptx->regmap, 0x0408, ANA_SB_RXTERM_OFFSP, + FIELD_PREP(ANA_SB_RXTERM_OFFSP, 0x3)); + regmap_update_bits(hdptx->regmap, 0x040c, ANA_SB_RXTERM_OFFSN, + FIELD_PREP(ANA_SB_RXTERM_OFFSN, 0x3)); + regmap_update_bits(hdptx->regmap, 0x047c, SB_RCAL_RSTN, + FIELD_PREP(SB_RCAL_RSTN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0410, SB_AUX_EN, + FIELD_PREP(SB_AUX_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0480, SB_AUX_EN_IN, + FIELD_PREP(SB_AUX_EN_IN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x040c, OVRD_SB_RX_RESCAL_DONE, + FIELD_PREP(OVRD_SB_RX_RESCAL_DONE, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0410, OVRD_SB_EN, + FIELD_PREP(OVRD_SB_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0408, OVRD_SB_RXTERM_EN, + FIELD_PREP(OVRD_SB_RXTERM_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x043c, OVRD_SB_VREG_EN, + FIELD_PREP(OVRD_SB_VREG_EN, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0410, OVRD_SB_AUX_EN, + FIELD_PREP(OVRD_SB_AUX_EN, 0x1)); + + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, BGR_EN, + FIELD_PREP(BGR_EN, 0x1)); + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, BIAS_EN, + FIELD_PREP(BIAS_EN, 0x1)); + udelay(20); + + reset_control_deassert(hdptx->init_reset); + udelay(20); + reset_control_deassert(hdptx->cmn_reset); + udelay(20); + + regmap_update_bits(hdptx->regmap, 0x040c, SB_RX_RESCAL_DONE, + FIELD_PREP(SB_RX_RESCAL_DONE, 0x1)); + udelay(100); + regmap_update_bits(hdptx->regmap, 0x0410, SB_EN, + FIELD_PREP(SB_EN, 0x1)); + udelay(100); + regmap_update_bits(hdptx->regmap, 0x0408, SB_RXRERM_EN, + FIELD_PREP(SB_RXRERM_EN, 0x1)); + udelay(20); + regmap_update_bits(hdptx->regmap, 0x043c, SB_VREG_EN, + FIELD_PREP(SB_VREG_EN, 0x1)); + udelay(20); + regmap_update_bits(hdptx->regmap, 0x0410, SB_AUX_EN, + FIELD_PREP(SB_AUX_EN, 0x1)); + udelay(100); + + ret = regmap_read_poll_timeout(hdptx->grf, HDPTXPHY_GRF_STATUS0, + status, FIELD_GET(SB_RDY, status), + 50, 1000); + if (ret) { + dev_err(hdptx->dev, "timeout waiting for sb_rdy\n"); + return ret; + } + + return 0; +} + +static void rockchip_hdptx_phy_reset(struct rockchip_hdptx_phy *hdptx) +{ + reset_control_assert(hdptx->lane_reset); + reset_control_assert(hdptx->cmn_reset); + reset_control_assert(hdptx->init_reset); + + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, PLL_EN, + FIELD_PREP(PLL_EN, 0)); + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, BIAS_EN, + FIELD_PREP(BIAS_EN, 0)); + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, BGR_EN, + FIELD_PREP(BGR_EN, 0)); +} + +static int rockchip_hdptx_phy_power_on(struct phy *phy) +{ + struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); + enum phy_mode mode = phy_get_mode(phy); + u32 lane; + int ret; + + ret = clk_bulk_prepare_enable(hdptx->nr_clks, hdptx->clks); + if (ret) + return ret; + + rockchip_hdptx_phy_reset(hdptx); + + reset_control_assert(hdptx->apb_reset); + udelay(10); + reset_control_deassert(hdptx->apb_reset); + + for (lane = 0; lane < 4; lane++) { + u32 invert = hdptx->lane_polarity_invert[lane]; + + regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c78), + LN_POLARITY_INV, + FIELD_PREP(LN_POLARITY_INV, invert)); + } + + if (mode == PHY_MODE_DP) { + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, + HDPTX_MODE_SEL, + FIELD_PREP(HDPTX_MODE_SEL, 0x1)); + + regmap_update_bits(hdptx->regmap, 0x0800, PROTOCOL_SEL, + FIELD_PREP(PROTOCOL_SEL, 0x0)); + regmap_update_bits(hdptx->regmap, 0x0818, DATA_BUS_WIDTH, + FIELD_PREP(DATA_BUS_WIDTH, 0x1)); + regmap_update_bits(hdptx->regmap, 0x0818, BUS_WIDTH_SEL, + FIELD_PREP(BUS_WIDTH_SEL, 0x0)); + + rockchip_hdptx_phy_dp_pll_init(hdptx); + rockchip_hdptx_phy_dp_aux_init(hdptx); + } else { + rockchip_grf_write(hdptx->grf, HDPTXPHY_GRF_CON0, + HDPTX_MODE_SEL, + FIELD_PREP(HDPTX_MODE_SEL, 0x0)); + + regmap_update_bits(hdptx->regmap, 0x0800, PROTOCOL_SEL, + FIELD_PREP(PROTOCOL_SEL, 0x1)); + } + + return 0; +} + +static int rockchip_hdptx_phy_power_off(struct phy *phy) +{ + struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); + + rockchip_hdptx_phy_reset(hdptx); + + clk_bulk_disable_unprepare(hdptx->nr_clks, hdptx->clks); + + return 0; +} + +static const struct phy_ops rockchip_hdptx_phy_ops = { + .set_mode = rockchip_hdptx_phy_set_mode, + .configure = rockchip_hdptx_phy_configure, + .power_on = rockchip_hdptx_phy_power_on, + .power_off = rockchip_hdptx_phy_power_off, + .owner = THIS_MODULE, +}; + +static bool rockchip_hdptx_phy_is_accissible_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case 0x0000 ... 0x029c: /* CMN Register */ + case 0x0400 ... 0x04a4: /* Sideband Register */ + case 0x0800 ... 0x08a4: /* Lane Top Register */ + case 0x0c00 ... 0x0cb4: /* Lane 0 Register */ + case 0x1000 ... 0x10b4: /* Lane 1 Register */ + case 0x1400 ... 0x14b4: /* Lane 2 Register */ + case 0x1800 ... 0x18b4: /* Lane 3 Register */ + return true; + default: + return false; + } +} + +static const struct regmap_config rockchip_hdptx_phy_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .max_register = 0x18b4, + .readable_reg = rockchip_hdptx_phy_is_accissible_reg, + .writeable_reg = rockchip_hdptx_phy_is_accissible_reg, +}; + +static int rockchip_hdptx_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_hdptx_phy *hdptx; + struct phy *phy; + struct phy_provider *phy_provider; + void __iomem *regs; + int ret; + + hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL); + if (!hdptx) + return -ENOMEM; + + hdptx->dev = dev; + platform_set_drvdata(pdev, hdptx); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + hdptx->regmap = devm_regmap_init_mmio(dev, regs, + &rockchip_hdptx_phy_regmap_config); + if (IS_ERR(hdptx->regmap)) + return dev_err_probe(dev, PTR_ERR(hdptx->regmap), + "failed to create regmap\n"); + + ret = devm_clk_bulk_get_all(dev, &hdptx->clks); + if (ret < 1) + return dev_err_probe(dev, ret, "failed to get clocks\n"); + + hdptx->nr_clks = ret; + + hdptx->apb_reset = devm_reset_control_get(dev, "apb"); + if (IS_ERR(hdptx->apb_reset)) + return dev_err_probe(dev, PTR_ERR(hdptx->apb_reset), + "failed to get apb reset\n"); + + hdptx->init_reset = devm_reset_control_get(dev, "init"); + if (IS_ERR(hdptx->init_reset)) + return dev_err_probe(dev, PTR_ERR(hdptx->init_reset), + "failed to get init reset\n"); + + hdptx->cmn_reset = devm_reset_control_get(dev, "cmn"); + if (IS_ERR(hdptx->cmn_reset)) + return dev_err_probe(dev, PTR_ERR(hdptx->cmn_reset), + "failed to get cmn reset\n"); + + hdptx->lane_reset = devm_reset_control_get(dev, "lane"); + if (IS_ERR(hdptx->lane_reset)) + return dev_err_probe(dev, PTR_ERR(hdptx->lane_reset), + "failed to get lane reset\n"); + + hdptx->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(hdptx->grf)) + return dev_err_probe(dev, PTR_ERR(hdptx->grf), + "failed to get grf regmap\n"); + + device_property_read_u32_array(dev, "lane-polarity-invert", + hdptx->lane_polarity_invert, 4); + + phy = devm_phy_create(dev, NULL, &rockchip_hdptx_phy_ops); + if (IS_ERR(phy)) { + ret = PTR_ERR(phy); + dev_err(dev, "failed to create PHY: %d\n", ret); + return ret; + } + + phy_set_drvdata(phy, hdptx); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_hdptx_phy_of_match[] = { + { .compatible = "rockchip,rk3588-hdptx-phy", }, + {} +}; +MODULE_DEVICE_TABLE(of, rockchip_hdptx_phy_of_match); + +static struct platform_driver rockchip_hdptx_phy_driver = { + .probe = rockchip_hdptx_phy_probe, + .driver = { + .name = "rockchip-hdptx-phy", + .of_match_table = rockchip_hdptx_phy_of_match, + } +}; +module_platform_driver(rockchip_hdptx_phy_driver); + +MODULE_AUTHOR("Wyon Bi "); +MODULE_DESCRIPTION("Rockchip HDMI/DP Combo PHY with Samsung IP block"); +MODULE_LICENSE("GPL v2");