#include <linux/reset.h>
#define GRF_HDPTX_CON0 0x00
+#define LC_REF_CLK_SEL BIT(11)
#define HDPTX_I_PLL_EN BIT(7)
#define HDPTX_I_BIAS_EN BIT(6)
#define HDPTX_I_BGR_EN BIT(5)
#define HDMI14_MAX_RATE 340000000
#define HDMI20_MAX_RATE 600000000
+#define FRL_3G3L_RATE 900000000
+#define FRL_6G3L_RATE 1800000000
+#define FRL_8G4L_RATE 3200000000
enum dp_link_rate {
DP_BW_RBR,
DP_BW_HBR2,
};
+struct lcpll_config {
+ unsigned long long rate;
+ u8 lcvco_mode_en;
+ u8 pi_en;
+ u8 clk_en_100m;
+ u8 pms_mdiv;
+ u8 pms_mdiv_afc;
+ u8 pms_pdiv;
+ u8 pms_refdiv;
+ u8 pms_sdiv;
+ u8 sdm_deno;
+ u8 sdm_num_sign;
+ u8 sdm_num;
+ u8 sdc_n;
+};
+
struct ropll_config {
unsigned long long rate;
u8 pms_mdiv;
};
struct rk_hdptx_hdmi_cfg {
+ enum phy_hdmi_mode mode;
unsigned long long rate;
unsigned int bpc;
};
unsigned int lanes;
};
+static const struct lcpll_config rk_hdptx_frl_lcpll_cfg[] = {
+ /* | pms | sdm | */
+ /* rate, lcen, pien, cken, mdiv, mdafc, pdiv, rdiv, sdiv, deno, nsig, num, sdcn, */
+ { 4800000000ULL, 1, 0, 0, 125, 125, 1, 1, 0, 1, 0, 0, 2, },
+ { 4000000000ULL, 1, 1, 0, 104, 104, 1, 1, 0, 9, 0, 1, 1, },
+ { 2400000000ULL, 1, 0, 0, 125, 125, 1, 1, 1, 1, 0, 0, 2, },
+ { 1800000000ULL, 1, 0, 0, 125, 125, 1, 1, 1, 1, 0, 0, 2, },
+ { 900000000ULL, 1, 0, 0, 125, 125, 1, 1, 3, 1, 0, 0, 2, },
+};
+
static const struct ropll_config rk_hdptx_tmds_ropll_cfg[] = {
/* | pms | sdm | sdc | */
/* rate, mdiv, mdafc, pdiv, rdiv, sdiv, en, deno, nsig, num, n, num, deno, */
REG_SEQ0(CMN_REG(009a), 0x11),
};
+static const struct reg_sequence rk_hdptx_frl_lcpll_cmn_init_seq[] = {
+ REG_SEQ0(CMN_REG(0011), 0x00),
+ REG_SEQ0(CMN_REG(0017), 0x00),
+ REG_SEQ0(CMN_REG(0025), 0x10),
+ REG_SEQ0(CMN_REG(0026), 0x53),
+ REG_SEQ0(CMN_REG(0027), 0x01),
+ REG_SEQ0(CMN_REG(0028), 0x0d),
+ REG_SEQ0(CMN_REG(002e), 0x02),
+ REG_SEQ0(CMN_REG(002f), 0x0d),
+ REG_SEQ0(CMN_REG(0030), 0x00),
+ REG_SEQ0(CMN_REG(0031), 0x20),
+ REG_SEQ0(CMN_REG(0032), 0x30),
+ REG_SEQ0(CMN_REG(0033), 0x0b),
+ REG_SEQ0(CMN_REG(0034), 0x23),
+ REG_SEQ0(CMN_REG(003d), 0x00),
+ REG_SEQ0(CMN_REG(0042), 0xb8),
+ REG_SEQ0(CMN_REG(0046), 0xff),
+ REG_SEQ0(CMN_REG(0048), 0x44),
+ REG_SEQ0(CMN_REG(004e), 0x14),
+ REG_SEQ0(CMN_REG(0051), 0x00),
+ REG_SEQ0(CMN_REG(0055), 0x00),
+ REG_SEQ0(CMN_REG(0059), 0x11),
+ REG_SEQ0(CMN_REG(005a), 0x03),
+ REG_SEQ0(CMN_REG(005c), 0x05),
+ REG_SEQ0(CMN_REG(005d), 0x0c),
+ REG_SEQ0(CMN_REG(005e), 0x07),
+ REG_SEQ0(CMN_REG(0060), 0x01),
+ REG_SEQ0(CMN_REG(0064), 0x07),
+ REG_SEQ0(CMN_REG(0065), 0x00),
+ REG_SEQ0(CMN_REG(0069), 0x00),
+ REG_SEQ0(CMN_REG(006b), 0x04),
+ REG_SEQ0(CMN_REG(006c), 0x00),
+ REG_SEQ0(CMN_REG(0070), 0x01),
+ REG_SEQ0(CMN_REG(0073), 0x30),
+ REG_SEQ0(CMN_REG(0074), 0x00),
+ REG_SEQ0(CMN_REG(0081), 0x09),
+ REG_SEQ0(CMN_REG(0086), 0x01),
+ REG_SEQ0(CMN_REG(0087), 0x0c),
+ REG_SEQ0(CMN_REG(0089), 0x02),
+ REG_SEQ0(CMN_REG(0095), 0x00),
+ REG_SEQ0(CMN_REG(0097), 0x00),
+ REG_SEQ0(CMN_REG(0099), 0x00),
+ REG_SEQ0(CMN_REG(009b), 0x10),
+};
+
+static const struct reg_sequence rk_hdptx_frl_lcpll_ropll_cmn_init_seq[] = {
+ REG_SEQ0(CMN_REG(0008), 0xd0),
+ REG_SEQ0(CMN_REG(0011), 0x00),
+ REG_SEQ0(CMN_REG(0017), 0x00),
+ REG_SEQ0(CMN_REG(001e), 0x35),
+ REG_SEQ0(CMN_REG(0020), 0x6b),
+ REG_SEQ0(CMN_REG(0021), 0x6b),
+ REG_SEQ0(CMN_REG(0022), 0x11),
+ REG_SEQ0(CMN_REG(0024), 0x00),
+ REG_SEQ0(CMN_REG(0025), 0x10),
+ REG_SEQ0(CMN_REG(0026), 0x53),
+ REG_SEQ0(CMN_REG(0027), 0x15),
+ REG_SEQ0(CMN_REG(0028), 0x0d),
+ REG_SEQ0(CMN_REG(002a), 0x09),
+ REG_SEQ0(CMN_REG(002b), 0x01),
+ REG_SEQ0(CMN_REG(002c), 0x02),
+ REG_SEQ0(CMN_REG(002d), 0x02),
+ REG_SEQ0(CMN_REG(002e), 0x0d),
+ REG_SEQ0(CMN_REG(002f), 0x61),
+ REG_SEQ0(CMN_REG(0030), 0x00),
+ REG_SEQ0(CMN_REG(0031), 0x20),
+ REG_SEQ0(CMN_REG(0032), 0x30),
+ REG_SEQ0(CMN_REG(0033), 0x0b),
+ REG_SEQ0(CMN_REG(0034), 0x23),
+ REG_SEQ0(CMN_REG(0037), 0x00),
+ REG_SEQ0(CMN_REG(003d), 0xc0),
+ REG_SEQ0(CMN_REG(0042), 0xb8),
+ REG_SEQ0(CMN_REG(0046), 0xff),
+ REG_SEQ0(CMN_REG(0048), 0x44),
+ REG_SEQ0(CMN_REG(004e), 0x14),
+ REG_SEQ0(CMN_REG(0054), 0x19),
+ REG_SEQ0(CMN_REG(0058), 0x19),
+ REG_SEQ0(CMN_REG(0059), 0x11),
+ REG_SEQ0(CMN_REG(005b), 0x30),
+ REG_SEQ0(CMN_REG(005c), 0x25),
+ REG_SEQ0(CMN_REG(005d), 0x14),
+ REG_SEQ0(CMN_REG(005e), 0x0e),
+ REG_SEQ0(CMN_REG(0063), 0x01),
+ REG_SEQ0(CMN_REG(0064), 0x0e),
+ REG_SEQ0(CMN_REG(0068), 0x00),
+ REG_SEQ0(CMN_REG(0069), 0x02),
+ REG_SEQ0(CMN_REG(006b), 0x00),
+ REG_SEQ0(CMN_REG(006f), 0x00),
+ REG_SEQ0(CMN_REG(0073), 0x02),
+ REG_SEQ0(CMN_REG(0074), 0x00),
+ REG_SEQ0(CMN_REG(007a), 0x00),
+ REG_SEQ0(CMN_REG(0081), 0x09),
+ REG_SEQ0(CMN_REG(0086), 0x11),
+ REG_SEQ0(CMN_REG(0087), 0x0c),
+ REG_SEQ0(CMN_REG(0089), 0x00),
+ REG_SEQ0(CMN_REG(0095), 0x03),
+ REG_SEQ0(CMN_REG(0097), 0x00),
+ REG_SEQ0(CMN_REG(0099), 0x00),
+ REG_SEQ0(CMN_REG(009b), 0x10),
+ REG_SEQ0(CMN_REG(009e), 0x03),
+ REG_SEQ0(CMN_REG(009f), 0xff),
+ REG_SEQ0(CMN_REG(00a0), 0x60),
+};
+
static const struct reg_sequence rk_hdptx_tmds_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0008), 0x00),
REG_SEQ0(CMN_REG(0011), 0x01),
REG_SEQ0(SB_REG(0117), 0x00),
};
+static const struct reg_sequence rk_hdptx_frl_lntop_init_seq[] = {
+ REG_SEQ0(LNTOP_REG(0200), 0x04),
+ REG_SEQ0(LNTOP_REG(0201), 0x00),
+ REG_SEQ0(LNTOP_REG(0202), 0x00),
+ REG_SEQ0(LNTOP_REG(0203), 0xf0),
+ REG_SEQ0(LNTOP_REG(0204), 0xff),
+ REG_SEQ0(LNTOP_REG(0205), 0xff),
+ REG_SEQ0(LNTOP_REG(0206), 0x05),
+};
+
static const struct reg_sequence rk_hdptx_tmds_lntop_highbr_seq[] = {
REG_SEQ0(LNTOP_REG(0201), 0x00),
REG_SEQ0(LNTOP_REG(0202), 0x00),
REG_SEQ0(LANE_REG(0620), 0xa0),
};
+static const struct reg_sequence rk_hdptx_frl_lane_init_seq[] = {
+ REG_SEQ0(LANE_REG(0312), 0x3c),
+ REG_SEQ0(LANE_REG(0412), 0x3c),
+ REG_SEQ0(LANE_REG(0512), 0x3c),
+ REG_SEQ0(LANE_REG(0612), 0x3c),
+ REG_SEQ0(LANE_REG(0303), 0x2f),
+ REG_SEQ0(LANE_REG(0403), 0x2f),
+ REG_SEQ0(LANE_REG(0503), 0x2f),
+ REG_SEQ0(LANE_REG(0603), 0x2f),
+ REG_SEQ0(LANE_REG(0305), 0x03),
+ REG_SEQ0(LANE_REG(0405), 0x03),
+ REG_SEQ0(LANE_REG(0505), 0x03),
+ REG_SEQ0(LANE_REG(0605), 0x03),
+ REG_SEQ0(LANE_REG(0306), 0xfc),
+ REG_SEQ0(LANE_REG(0406), 0xfc),
+ REG_SEQ0(LANE_REG(0506), 0xfc),
+ REG_SEQ0(LANE_REG(0606), 0xfc),
+ REG_SEQ0(LANE_REG(0305), 0x4f),
+ REG_SEQ0(LANE_REG(0405), 0x4f),
+ REG_SEQ0(LANE_REG(0505), 0x4f),
+ REG_SEQ0(LANE_REG(0605), 0x4f),
+ REG_SEQ0(LANE_REG(0304), 0x14),
+ REG_SEQ0(LANE_REG(0404), 0x14),
+ REG_SEQ0(LANE_REG(0504), 0x14),
+ REG_SEQ0(LANE_REG(0604), 0x14),
+ /* Keep Inter-Pair Skew in the limits */
+ REG_SEQ0(LANE_REG(031e), 0x02),
+ REG_SEQ0(LANE_REG(041e), 0x02),
+ REG_SEQ0(LANE_REG(051e), 0x02),
+ REG_SEQ0(LANE_REG(061e), 0x02),
+};
+
static const struct reg_sequence rk_hdptx_tmds_lane_init_seq[] = {
REG_SEQ0(LANE_REG(0312), 0x00),
REG_SEQ0(LANE_REG(0412), 0x00),
HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN;
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
- regmap_write(hdptx->regmap, LNTOP_REG(0207), 0x0f);
+ /* 3 lanes FRL mode */
+ if (hdptx->hdmi_cfg.rate == FRL_6G3L_RATE ||
+ hdptx->hdmi_cfg.rate == FRL_3G3L_RATE)
+ regmap_write(hdptx->regmap, LNTOP_REG(0207), 0x07);
+ else
+ regmap_write(hdptx->regmap, LNTOP_REG(0207), 0x0f);
ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS, val,
(val & HDPTX_O_PHY_RDY) &&
return true;
}
+static int rk_hdptx_frl_lcpll_cmn_config(struct rk_hdptx_phy *hdptx)
+{
+ const struct lcpll_config *cfg = NULL;
+ int i;
+
+ dev_dbg(hdptx->dev, "%s rate=%llu\n", __func__, hdptx->hdmi_cfg.rate);
+
+ for (i = 0; i < ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg); i++) {
+ if (hdptx->hdmi_cfg.rate == rk_hdptx_frl_lcpll_cfg[i].rate) {
+ cfg = &rk_hdptx_frl_lcpll_cfg[i];
+ break;
+ }
+ }
+
+ if (!cfg) {
+ dev_err(hdptx->dev, "%s cannot find pll cfg for rate=%llu\n",
+ __func__, hdptx->hdmi_cfg.rate);
+ return -EINVAL;
+ }
+
+ rk_hdptx_pre_power_up(hdptx);
+
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, LC_REF_CLK_SEL << 16);
+
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq);
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lcpll_cmn_init_seq);
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0008),
+ LCPLL_EN_MASK | LCPLL_LCVCO_MODE_EN_MASK,
+ FIELD_PREP(LCPLL_EN_MASK, 1) |
+ FIELD_PREP(LCPLL_LCVCO_MODE_EN_MASK, cfg->lcvco_mode_en));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(001e),
+ LCPLL_PI_EN_MASK | LCPLL_100M_CLK_EN_MASK,
+ FIELD_PREP(LCPLL_PI_EN_MASK, cfg->pi_en) |
+ FIELD_PREP(LCPLL_100M_CLK_EN_MASK, cfg->clk_en_100m));
+
+ regmap_write(hdptx->regmap, CMN_REG(0020), cfg->pms_mdiv);
+ regmap_write(hdptx->regmap, CMN_REG(0021), cfg->pms_mdiv_afc);
+ regmap_write(hdptx->regmap, CMN_REG(0022),
+ (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
+ regmap_write(hdptx->regmap, CMN_REG(0023),
+ (cfg->pms_sdiv << 4) | cfg->pms_sdiv);
+ regmap_write(hdptx->regmap, CMN_REG(002a), cfg->sdm_deno);
+ regmap_write(hdptx->regmap, CMN_REG(002b), cfg->sdm_num_sign);
+ regmap_write(hdptx->regmap, CMN_REG(002c), cfg->sdm_num);
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(002d), LCPLL_SDC_N_MASK,
+ FIELD_PREP(LCPLL_SDC_N_MASK, cfg->sdc_n));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_POSTDIV_SEL_MASK,
+ FIELD_PREP(PLL_PCG_POSTDIV_SEL_MASK, cfg->pms_sdiv));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_SEL_MASK,
+ FIELD_PREP(PLL_PCG_CLK_SEL_MASK, (hdptx->hdmi_cfg.bpc - 8) >> 1));
+
+ return rk_hdptx_post_enable_pll(hdptx);
+}
+
+static int rk_hdptx_frl_lcpll_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
+{
+ dev_dbg(hdptx->dev, "%s rate=%llu\n", __func__, hdptx->hdmi_cfg.rate);
+
+ rk_hdptx_pre_power_up(hdptx);
+
+ /* ROPLL input reference clock from LCPLL (cascade mode) */
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ (LC_REF_CLK_SEL << 16) | LC_REF_CLK_SEL);
+
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq);
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lcpll_ropll_cmn_init_seq);
+
+ return rk_hdptx_post_enable_pll(hdptx);
+}
+
static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
{
const struct ropll_config *cfg = NULL;
rk_hdptx_pre_power_up(hdptx);
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, LC_REF_CLK_SEL << 16);
+
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq);
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_tmds_cmn_init_seq);
return rk_hdptx_post_enable_pll(hdptx);
}
+static int rk_hdptx_pll_cmn_config(struct rk_hdptx_phy *hdptx)
+{
+ if (hdptx->hdmi_cfg.rate <= HDMI20_MAX_RATE)
+ return rk_hdptx_tmds_ropll_cmn_config(hdptx);
+
+ if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE)
+ return rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx);
+
+ return rk_hdptx_frl_lcpll_cmn_config(hdptx);
+}
+
+static int rk_hdptx_frl_lcpll_mode_config(struct rk_hdptx_phy *hdptx)
+{
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_sb_init_seq);
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lntop_init_seq);
+
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_lane_init_seq);
+ rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lane_init_seq);
+
+ return rk_hdptx_post_enable_lane(hdptx);
+}
+
static int rk_hdptx_tmds_ropll_mode_config(struct rk_hdptx_phy *hdptx)
{
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_sb_init_seq);
if (mode == PHY_MODE_DP) {
rk_hdptx_dp_reset(hdptx);
} else {
- ret = rk_hdptx_tmds_ropll_cmn_config(hdptx);
+ ret = rk_hdptx_pll_cmn_config(hdptx);
if (ret)
goto dec_usage;
}
int ret, lane;
if (mode != PHY_MODE_DP) {
- if (!hdptx->hdmi_cfg.rate) {
+ if (!hdptx->hdmi_cfg.rate && hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL) {
/*
* FIXME: Temporary workaround to setup TMDS char rate
* from the RK DW HDMI QP bridge driver.
regmap_write(hdptx->grf, GRF_HDPTX_CON0,
HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
- ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
+ ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
+ else
+ ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
+
if (ret)
rk_hdptx_phy_consumer_put(hdptx, true);
}
{
int i;
- if (!hdmi_in->tmds_char_rate || hdmi_in->tmds_char_rate > HDMI20_MAX_RATE)
- return -EINVAL;
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) {
+ unsigned long long frl_rate = 100000000ULL * hdmi_in->frl.lanes *
+ hdmi_in->frl.rate_per_lane;
- for (i = 0; i < ARRAY_SIZE(rk_hdptx_tmds_ropll_cfg); i++)
- if (hdmi_in->tmds_char_rate == rk_hdptx_tmds_ropll_cfg[i].rate)
+ switch (hdmi_in->frl.rate_per_lane) {
+ case 3:
+ case 6:
+ case 8:
+ case 10:
+ case 12:
break;
+ default:
+ return -EINVAL;
+ }
- if (i == ARRAY_SIZE(rk_hdptx_tmds_ropll_cfg) &&
- !rk_hdptx_phy_clk_pll_calc(hdmi_in->tmds_char_rate, NULL))
- return -EINVAL;
+ if (!hdmi_in->frl.lanes || hdmi_in->frl.lanes > 4)
+ return -EINVAL;
+
+ if (frl_rate != FRL_8G4L_RATE) {
+ for (i = 0; i < ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg); i++)
+ if (frl_rate == rk_hdptx_frl_lcpll_cfg[i].rate)
+ break;
+ if (i == ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg))
+ return -EINVAL;
+ }
+
+ if (hdmi_out)
+ hdmi_out->rate = frl_rate;
+ } else {
+ if (!hdmi_in->tmds_char_rate || hdmi_in->tmds_char_rate > HDMI20_MAX_RATE)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(rk_hdptx_tmds_ropll_cfg); i++)
+ if (hdmi_in->tmds_char_rate == rk_hdptx_tmds_ropll_cfg[i].rate)
+ break;
+
+ if (i == ARRAY_SIZE(rk_hdptx_tmds_ropll_cfg) &&
+ !rk_hdptx_phy_clk_pll_calc(hdmi_in->tmds_char_rate, NULL))
+ return -EINVAL;
+
+ if (hdmi_out)
+ hdmi_out->rate = hdmi_in->tmds_char_rate;
+ }
switch (hdmi_in->bpc) {
case 0:
return -EINVAL;
}
- if (hdmi_out) {
- hdmi_out->rate = hdmi_in->tmds_char_rate;
+ if (hdmi_out)
hdmi_out->bpc = hdmi_in->bpc ?: 8;
- }
return 0;
}
return 0;
}
+static int rk_hdptx_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
+
+ if (mode == PHY_MODE_DP)
+ return 0;
+
+ if (mode != PHY_MODE_HDMI) {
+ dev_err(&phy->dev, "invalid PHY mode: %d\n", mode);
+ return -EINVAL;
+ }
+
+ switch (submode) {
+ case PHY_HDMI_MODE_TMDS:
+ case PHY_HDMI_MODE_FRL:
+ hdptx->hdmi_cfg.mode = submode;
+ break;
+ default:
+ dev_err(&phy->dev, "invalid HDMI mode: %d\n", submode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
static const struct phy_ops rk_hdptx_phy_ops = {
.power_on = rk_hdptx_phy_power_on,
.power_off = rk_hdptx_phy_power_off,
+ .set_mode = rk_hdptx_phy_set_mode,
.configure = rk_hdptx_phy_configure,
.validate = rk_hdptx_phy_validate,
.owner = THIS_MODULE,
static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
{
+ struct lcpll_config lcpll_hw;
struct ropll_config ropll_hw;
u64 fout, sdm;
u32 mode, val;
- int ret;
+ int ret, i;
ret = regmap_read(hdptx->regmap, CMN_REG(0008), &mode);
if (ret)
return 0;
- if (mode & LCPLL_LCVCO_MODE_EN_MASK)
+ if (mode & LCPLL_LCVCO_MODE_EN_MASK) {
+ ret = regmap_read(hdptx->regmap, CMN_REG(0020), &val);
+ if (ret)
+ return 0;
+ lcpll_hw.pms_mdiv = val;
+
+ ret = regmap_read(hdptx->regmap, CMN_REG(0023), &val);
+ if (ret)
+ return 0;
+ lcpll_hw.pms_sdiv = val & 0xf;
+
+ ret = regmap_read(hdptx->regmap, CMN_REG(002B), &val);
+ if (ret)
+ return 0;
+ lcpll_hw.sdm_num_sign = val;
+
+ ret = regmap_read(hdptx->regmap, CMN_REG(002C), &val);
+ if (ret)
+ return 0;
+ lcpll_hw.sdm_num = val;
+
+ ret = regmap_read(hdptx->regmap, CMN_REG(002A), &val);
+ if (ret)
+ return 0;
+ lcpll_hw.sdm_deno = val;
+
+ ret = regmap_read(hdptx->regmap, CMN_REG(002D), &val);
+ if (ret)
+ return 0;
+ lcpll_hw.sdc_n = (val & LCPLL_SDC_N_MASK) >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg); i++) {
+ const struct lcpll_config *cfg = &rk_hdptx_frl_lcpll_cfg[i];
+
+ if (cfg->pms_mdiv == lcpll_hw.pms_mdiv &&
+ cfg->pms_sdiv == lcpll_hw.pms_sdiv &&
+ cfg->sdm_num_sign == lcpll_hw.sdm_num_sign &&
+ cfg->sdm_num == lcpll_hw.sdm_num &&
+ cfg->sdm_deno == lcpll_hw.sdm_deno &&
+ cfg->sdc_n == lcpll_hw.sdc_n)
+ return cfg->rate;
+ }
+
+ dev_dbg(hdptx->dev, "%s no FRL match found\n", __func__);
return 0;
+ }
ret = regmap_read(hdptx->regmap, CMN_REG(0051), &val);
if (ret)
rate = rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
+ return rate;
+
return DIV_ROUND_CLOSEST_ULL(rate * 8, hdptx->hdmi_cfg.bpc);
}
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
+ return hdptx->hdmi_cfg.rate;
+
/*
* FIXME: Temporarily allow altering TMDS char rate via CCF.
* To be dropped as soon as the RK DW HDMI QP bridge driver
unsigned long parent_rate)
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- unsigned long long tmds_rate = DIV_ROUND_CLOSEST_ULL(rate * hdptx->hdmi_cfg.bpc, 8);
+ unsigned long long link_rate = rate;
+
+ if (hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL)
+ link_rate = DIV_ROUND_CLOSEST_ULL(rate * hdptx->hdmi_cfg.bpc, 8);
- /* Revert any unlikely TMDS char rate change since determine_rate() */
- if (hdptx->hdmi_cfg.rate != tmds_rate) {
+ /* Revert any unlikely link rate change since determine_rate() */
+ if (hdptx->hdmi_cfg.rate != link_rate) {
dev_warn(hdptx->dev, "Reverting unexpected rate change from %llu to %llu\n",
- tmds_rate, hdptx->hdmi_cfg.rate);
- hdptx->hdmi_cfg.rate = tmds_rate;
+ link_rate, hdptx->hdmi_cfg.rate);
+ hdptx->hdmi_cfg.rate = link_rate;
}
/*
- * The TMDS char rate would be normally programmed in HW during
+ * The link rate would be normally programmed in HW during
* phy_ops.power_on() or clk_ops.prepare() callbacks, but it might
* happen that the former gets fired too late, i.e. after this call,
* while the latter being executed only once, i.e. when clock remains
* in the prepared state during rate changes.
*/
- return rk_hdptx_tmds_ropll_cmn_config(hdptx);
+ return rk_hdptx_pll_cmn_config(hdptx);
}
static const struct clk_ops hdptx_phy_clk_ops = {