494 lines
16 KiB
Diff
494 lines
16 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Paolo Sabatino <[email protected]>
|
|
Date: Sun, 30 Jun 2024 17:18:22 +0200
|
|
Subject: add rk3228 support to inno hdmi driver
|
|
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 379 ++++++++++
|
|
1 file changed, 379 insertions(+)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -19,6 +19,110 @@
|
|
|
|
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
|
|
|
|
+/*
|
|
+ * ---- Registers for RK322x
|
|
+ */
|
|
+/* REG: 0x00 */
|
|
+#define RK3228_PRE_PLL_REFCLK_SEL_MASK BIT(0)
|
|
+#define RK3228_PRE_PLL_REFCLK_SEL_PCLK BIT(0)
|
|
+#define RK3228_PRE_PLL_REFCLK_SEL_OSCCLK 0
|
|
+/* REG: 0x01 */
|
|
+#define RK3228_BYPASS_RXSENSE_EN BIT(2)
|
|
+#define RK3228_BYPASS_PWRON_EN BIT(1)
|
|
+#define RK3228_BYPASS_PLLPD_EN BIT(0)
|
|
+/* REG: 0x02 */
|
|
+#define RK3228_INT_POL_HIGH BIT(7)
|
|
+#define RK3228_BYPASS_PDATA_EN BIT(4)
|
|
+#define RK3328_PDATA_EN BIT(0)
|
|
+#define RK3228_PDATA_EN_DISABLE BIT(0)
|
|
+/* REG: 0x03 */
|
|
+#define RK3228_BYPASS_AUTO_TERM_RES_CAL BIT(7)
|
|
+#define RK3228_AUDO_TERM_RES_CAL_SPEED_14_8(x) UPDATE(x, 6, 0)
|
|
+/* REG: 0x04 */
|
|
+#define RK3228_AUDO_TERM_RES_CAL_SPEED_7_0(x) UPDATE(x, 7, 0)
|
|
+/* REG: 0xaa */
|
|
+#define RK3228_POST_PLL_CTRL_MANUAL BIT(0)
|
|
+/* REG: 0xe0 */
|
|
+#define RK3228_POST_PLL_POWER_DOWN BIT(5)
|
|
+#define RK3228_POST_PLL_POWER_UP 0
|
|
+#define RK3228_PRE_PLL_POWER_DOWN BIT(4)
|
|
+#define RK3228_RXSENSE_CLK_CH_MASK BIT(3)
|
|
+#define RK3228_RXSENSE_CLK_CH_ENABLE BIT(3)
|
|
+#define RK3228_RXSENSE_DATA_CH2_MASK BIT(2)
|
|
+#define RK3228_RXSENSE_DATA_CH2_ENABLE BIT(2)
|
|
+#define RK3228_RXSENSE_DATA_CH1_MASK BIT(1)
|
|
+#define RK3228_RXSENSE_DATA_CH1_ENABLE BIT(1)
|
|
+#define RK3228_RXSENSE_DATA_CH0_MASK BIT(0)
|
|
+#define RK3228_RXSENSE_DATA_CH0_ENABLE BIT(0)
|
|
+/* REG: 0xe1 */
|
|
+#define RK3228_BANDGAP_ENABLE BIT(4)
|
|
+#define RK3228_TMDS_DRIVER_ENABLE GENMASK(3, 0)
|
|
+/* REG: 0xe2 */
|
|
+#define RK3228_PRE_PLL_FB_DIV_8_MASK BIT(7)
|
|
+#define RK3228_PRE_PLL_FB_DIV_8(x) UPDATE((x) >> 8, 7, 7)
|
|
+#define RK3228_PCLK_VCO_DIV_5_MASK BIT(5)
|
|
+#define RK3228_PCLK_VCO_DIV_5(x) UPDATE(x, 5, 5)
|
|
+#define RK3228_PRE_PLL_PRE_DIV_MASK GENMASK(4, 0)
|
|
+#define RK3228_PRE_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
|
|
+/* REG: 0xe3 */
|
|
+#define RK3228_PRE_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
|
|
+/* REG: 0xe4 */
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_B_MASK GENMASK(6, 5)
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_B_SHIFT 5
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_B(x) UPDATE(x, 6, 5)
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_A_MASK GENMASK(4, 0)
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_A(x) UPDATE(x, 4, 0)
|
|
+/* REG: 0xe5 */
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_C_MASK GENMASK(6, 5)
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_C(x) UPDATE(x, 6, 5)
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_D_MASK GENMASK(4, 0)
|
|
+#define RK3228_PRE_PLL_PCLK_DIV_D(x) UPDATE(x, 4, 0)
|
|
+/* REG: 0xe6 */
|
|
+#define RK3228_PRE_PLL_TMDSCLK_DIV_C_MASK GENMASK(5, 4)
|
|
+#define RK3228_PRE_PLL_TMDSCLK_DIV_C(x) UPDATE(x, 5, 4)
|
|
+#define RK3228_PRE_PLL_TMDSCLK_DIV_A_MASK GENMASK(3, 2)
|
|
+#define RK3228_PRE_PLL_TMDSCLK_DIV_A(x) UPDATE(x, 3, 2)
|
|
+#define RK3228_PRE_PLL_TMDSCLK_DIV_B_MASK GENMASK(1, 0)
|
|
+#define RK3228_PRE_PLL_TMDSCLK_DIV_B(x) UPDATE(x, 1, 0)
|
|
+/* REG: 0xe8 */
|
|
+#define RK3228_PRE_PLL_LOCK_STATUS BIT(0)
|
|
+/* REG: 0xe9 */
|
|
+#define RK3228_POST_PLL_POST_DIV_ENABLE UPDATE(3, 7, 6)
|
|
+#define RK3228_POST_PLL_PRE_DIV_MASK GENMASK(4, 0)
|
|
+#define RK3228_POST_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
|
|
+/* REG: 0xea */
|
|
+#define RK3228_POST_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
|
|
+/* REG: 0xeb */
|
|
+#define RK3228_POST_PLL_FB_DIV_8_MASK BIT(7)
|
|
+#define RK3228_POST_PLL_FB_DIV_8(x) UPDATE((x) >> 8, 7, 7)
|
|
+#define RK3228_POST_PLL_POST_DIV_MASK GENMASK(5, 4)
|
|
+#define RK3228_POST_PLL_POST_DIV(x) UPDATE(x, 5, 4)
|
|
+#define RK3228_POST_PLL_LOCK_STATUS BIT(0)
|
|
+/* REG: 0xee */
|
|
+#define RK3228_TMDS_CH_TA_ENABLE GENMASK(7, 4)
|
|
+/* REG: 0xef */
|
|
+#define RK3228_TMDS_CLK_CH_TA(x) UPDATE(x, 7, 6)
|
|
+#define RK3228_TMDS_DATA_CH2_TA(x) UPDATE(x, 5, 4)
|
|
+#define RK3228_TMDS_DATA_CH1_TA(x) UPDATE(x, 3, 2)
|
|
+#define RK3228_TMDS_DATA_CH0_TA(x) UPDATE(x, 1, 0)
|
|
+/* REG: 0xf0 */
|
|
+#define RK3228_TMDS_DATA_CH2_PRE_EMPHASIS_MASK GENMASK(5, 4)
|
|
+#define RK3228_TMDS_DATA_CH2_PRE_EMPHASIS(x) UPDATE(x, 5, 4)
|
|
+#define RK3228_TMDS_DATA_CH1_PRE_EMPHASIS_MASK GENMASK(3, 2)
|
|
+#define RK3228_TMDS_DATA_CH1_PRE_EMPHASIS(x) UPDATE(x, 3, 2)
|
|
+#define RK3228_TMDS_DATA_CH0_PRE_EMPHASIS_MASK GENMASK(1, 0)
|
|
+#define RK3228_TMDS_DATA_CH0_PRE_EMPHASIS(x) UPDATE(x, 1, 0)
|
|
+/* REG: 0xf1 */
|
|
+#define RK3228_TMDS_CLK_CH_OUTPUT_SWING(x) UPDATE(x, 7, 4)
|
|
+#define RK3228_TMDS_DATA_CH2_OUTPUT_SWING(x) UPDATE(x, 3, 0)
|
|
+/* REG: 0xf2 */
|
|
+#define RK3228_TMDS_DATA_CH1_OUTPUT_SWING(x) UPDATE(x, 7, 4)
|
|
+#define RK3228_TMDS_DATA_CH0_OUTPUT_SWING(x) UPDATE(x, 3, 0)
|
|
+
|
|
+
|
|
+/*
|
|
+ * ---- Registers for RK3328
|
|
+ */
|
|
/* REG: 0x01 */
|
|
#define RK3328_BYPASS_RXSENSE_EN BIT(2)
|
|
#define RK3328_BYPASS_POWERON_EN BIT(1)
|
|
@@ -177,6 +281,7 @@ struct inno_hdmi_phy_plat_ops {
|
|
};
|
|
|
|
enum inno_hdmi_phy_type {
|
|
+ INNO_HDMI_PHY_RK3228,
|
|
INNO_HDMI_PHY_RK3328,
|
|
};
|
|
|
|
@@ -388,6 +493,25 @@ static const struct post_pll_config post_pll_cfg_table[] = {
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
+/* phy tuning values for an undocumented set of registers */
|
|
+static const struct phy_config rk3228_phy_cfg[] = {
|
|
+ { 165000000, {
|
|
+ 0xaa, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ },
|
|
+ }, {
|
|
+ 340000000, {
|
|
+ 0xaa, 0x15, 0x6a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ },
|
|
+ }, {
|
|
+ 594000000, {
|
|
+ 0xaa, 0x15, 0x7a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ },
|
|
+ }, { /* sentinel */ },
|
|
+};
|
|
+
|
|
/* phy tuning values for an undocumented set of registers */
|
|
static const struct phy_config rk3328_phy_cfg[] = {
|
|
{ 165000000, {
|
|
@@ -453,6 +577,46 @@ static unsigned long inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
|
|
}
|
|
}
|
|
|
|
+static
|
|
+unsigned long inno_hdmi_phy_rk3228_clk_recalc_rate(struct phy *phy,
|
|
+ unsigned long parent_rate)
|
|
+{
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+ u64 rate, vco;
|
|
+ u8 nd, no_a, no_b, no_d;
|
|
+ __maybe_unused u8 no_c;
|
|
+ u16 nf;
|
|
+
|
|
+ nd = inno_read(inno, 0xe2) & RK3228_PRE_PLL_PRE_DIV_MASK;
|
|
+ nf = (inno_read(inno, 0xe2) & RK3228_PRE_PLL_FB_DIV_8_MASK) << 1;
|
|
+ nf |= inno_read(inno, 0xe3);
|
|
+ vco = parent_rate * nf;
|
|
+
|
|
+ if (inno_read(inno, 0xe2) & RK3228_PCLK_VCO_DIV_5_MASK) {
|
|
+ rate = vco / (nd * 5);
|
|
+ } else {
|
|
+ no_a = inno_read(inno, 0xe4) & RK3228_PRE_PLL_PCLK_DIV_A_MASK;
|
|
+ if (!no_a)
|
|
+ no_a = 1;
|
|
+ no_b = inno_read(inno, 0xe4) & RK3228_PRE_PLL_PCLK_DIV_B_MASK;
|
|
+ no_b >>= RK3228_PRE_PLL_PCLK_DIV_B_SHIFT;
|
|
+ no_b += 2;
|
|
+ no_d = inno_read(inno, 0xe5) & RK3228_PRE_PLL_PCLK_DIV_D_MASK;
|
|
+
|
|
+ if (no_a == 1)
|
|
+ rate = vco / (nd * no_b * no_d * 2);
|
|
+ else
|
|
+ rate = vco / (nd * no_a * no_d * 2);
|
|
+
|
|
+ }
|
|
+
|
|
+ inno->pixclock = rate;
|
|
+
|
|
+ dev_info(phy->dev, "rate %lu vco %llu\n", inno->pixclock, vco);
|
|
+
|
|
+ return inno->pixclock;
|
|
+}
|
|
+
|
|
static
|
|
unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct phy *phy,
|
|
unsigned long parent_rate)
|
|
@@ -494,6 +658,23 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct phy *phy,
|
|
return inno->pixclock;
|
|
}
|
|
|
|
+static long inno_hdmi_phy_rk3228_clk_round_rate(struct phy *phy,
|
|
+ unsigned long rate)
|
|
+{
|
|
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
|
|
+
|
|
+ rate = (rate / 1000) * 1000;
|
|
+
|
|
+ for (; cfg->pixclock != 0; cfg++)
|
|
+ if (cfg->pixclock == rate)
|
|
+ break;
|
|
+
|
|
+ if (cfg->pixclock == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return cfg->pixclock;
|
|
+}
|
|
+
|
|
static long inno_hdmi_phy_rk3328_clk_round_rate(struct phy *phy,
|
|
unsigned long rate)
|
|
{
|
|
@@ -528,6 +709,71 @@ struct pre_pll_config *inno_hdmi_phy_get_pre_pll_cfg(struct inno_hdmi_phy *inno,
|
|
return cfg;
|
|
}
|
|
|
|
+static int
|
|
+inno_hdmi_phy_rk3228_clk_set_rate(struct phy *phy,
|
|
+ unsigned long rate,
|
|
+ unsigned long parent_rate)
|
|
+{
|
|
+
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+ unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
|
|
+ const struct pre_pll_config *cfg;
|
|
+
|
|
+ int ret;
|
|
+ u32 val;
|
|
+
|
|
+ dev_info(phy->dev, "rate %lu tmdsclk %lu\n", rate, tmdsclock);
|
|
+
|
|
+ if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
|
|
+ return 0;
|
|
+
|
|
+ cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
|
|
+ if (IS_ERR(cfg))
|
|
+ return PTR_ERR(cfg);
|
|
+
|
|
+ /* Power down PRE-PLL */
|
|
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN,
|
|
+ RK3228_PRE_PLL_POWER_DOWN);
|
|
+
|
|
+ inno_update_bits(inno, 0xe2, RK3228_PRE_PLL_FB_DIV_8_MASK |
|
|
+ RK3228_PCLK_VCO_DIV_5_MASK |
|
|
+ RK3228_PRE_PLL_PRE_DIV_MASK,
|
|
+ RK3228_PRE_PLL_FB_DIV_8(cfg->fbdiv) |
|
|
+ RK3228_PCLK_VCO_DIV_5(cfg->vco_div_5_en) |
|
|
+ RK3228_PRE_PLL_PRE_DIV(cfg->prediv));
|
|
+ inno_write(inno, 0xe3, RK3228_PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
|
|
+ inno_update_bits(inno, 0xe4, RK3228_PRE_PLL_PCLK_DIV_B_MASK |
|
|
+ RK3228_PRE_PLL_PCLK_DIV_A_MASK,
|
|
+ RK3228_PRE_PLL_PCLK_DIV_B(cfg->pclk_div_b) |
|
|
+ RK3228_PRE_PLL_PCLK_DIV_A(cfg->pclk_div_a));
|
|
+ inno_update_bits(inno, 0xe5, RK3228_PRE_PLL_PCLK_DIV_C_MASK |
|
|
+ RK3228_PRE_PLL_PCLK_DIV_D_MASK,
|
|
+ RK3228_PRE_PLL_PCLK_DIV_C(cfg->pclk_div_c) |
|
|
+ RK3228_PRE_PLL_PCLK_DIV_D(cfg->pclk_div_d));
|
|
+ inno_update_bits(inno, 0xe6, RK3228_PRE_PLL_TMDSCLK_DIV_C_MASK |
|
|
+ RK3228_PRE_PLL_TMDSCLK_DIV_A_MASK |
|
|
+ RK3228_PRE_PLL_TMDSCLK_DIV_B_MASK,
|
|
+ RK3228_PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c) |
|
|
+ RK3228_PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
|
|
+ RK3228_PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b));
|
|
+
|
|
+ /* Power up PRE-PLL */
|
|
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN, 0);
|
|
+
|
|
+ /* Wait for Pre-PLL lock */
|
|
+ ret = inno_poll(inno, 0xe8, val, val & RK3228_PRE_PLL_LOCK_STATUS,
|
|
+ 1000, 10000);
|
|
+ if (ret) {
|
|
+ dev_err(phy->dev, "Pre-PLL locking failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ inno->pixclock = rate;
|
|
+ inno->tmdsclock = tmdsclock;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
inno_hdmi_phy_rk3328_clk_set_rate(struct phy *phy,
|
|
unsigned long rate,
|
|
@@ -588,6 +834,13 @@ inno_hdmi_phy_rk3328_clk_set_rate(struct phy *phy,
|
|
return 0;
|
|
}
|
|
|
|
+static void inno_hdmi_phy_rk3228_clk_enable(struct phy *phy)
|
|
+{
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+
|
|
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN, 0);
|
|
+}
|
|
+
|
|
static void inno_hdmi_phy_rk3328_clk_enable(struct phy *phy)
|
|
{
|
|
struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
@@ -595,6 +848,14 @@ static void inno_hdmi_phy_rk3328_clk_enable(struct phy *phy)
|
|
inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN, 0);
|
|
}
|
|
|
|
+static void inno_hdmi_phy_rk3228_clk_disable(struct phy *phy)
|
|
+{
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+
|
|
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN,
|
|
+ RK3228_PRE_PLL_POWER_DOWN);
|
|
+}
|
|
+
|
|
static void inno_hdmi_phy_rk3328_clk_disable(struct phy *phy)
|
|
{
|
|
struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
@@ -603,6 +864,65 @@ static void inno_hdmi_phy_rk3328_clk_disable(struct phy *phy)
|
|
RK3328_PRE_PLL_POWER_DOWN);
|
|
}
|
|
|
|
+static int
|
|
+inno_hdmi_phy_rk3228_power_on(struct phy *phy,
|
|
+ const struct post_pll_config *cfg,
|
|
+ const struct phy_config *phy_cfg)
|
|
+{
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+ u32 val;
|
|
+ int ret;
|
|
+
|
|
+ /* set pdata_en to 0 */
|
|
+ inno_update_bits(inno, 0x02, RK3228_PDATA_EN_DISABLE, RK3228_PDATA_EN_DISABLE);
|
|
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN | RK3228_POST_PLL_POWER_DOWN,
|
|
+ RK3228_PRE_PLL_POWER_DOWN | RK3228_POST_PLL_POWER_DOWN);
|
|
+
|
|
+ /* Post-PLL update */
|
|
+ inno_update_bits(inno, 0xe9, RK3228_POST_PLL_PRE_DIV_MASK,
|
|
+ RK3228_POST_PLL_PRE_DIV(cfg->prediv));
|
|
+ inno_update_bits(inno, 0xeb, RK3228_POST_PLL_FB_DIV_8_MASK,
|
|
+ RK3228_POST_PLL_FB_DIV_8(cfg->fbdiv));
|
|
+ inno_write(inno, 0xea, RK3228_POST_PLL_FB_DIV_7_0(cfg->fbdiv));
|
|
+
|
|
+ if (cfg->postdiv == 1) {
|
|
+ inno_update_bits(inno, 0xe9, RK3228_POST_PLL_POST_DIV_ENABLE, 0);
|
|
+ } else {
|
|
+ int div = cfg->postdiv / 2 - 1;
|
|
+
|
|
+ inno_update_bits(inno, 0xe9, RK3228_POST_PLL_POST_DIV_ENABLE,
|
|
+ RK3228_POST_PLL_POST_DIV_ENABLE);
|
|
+ inno_update_bits(inno, 0xeb, RK3228_POST_PLL_POST_DIV_MASK,
|
|
+ RK3228_POST_PLL_POST_DIV(div));
|
|
+ }
|
|
+
|
|
+ for (val = 0; val < 4; val++)
|
|
+ inno_write(inno, 0xef + val, phy_cfg->regs[val]);
|
|
+
|
|
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN |
|
|
+ RK3228_POST_PLL_POWER_DOWN, 0);
|
|
+ inno_update_bits(inno, 0xe1, RK3228_BANDGAP_ENABLE,
|
|
+ RK3228_BANDGAP_ENABLE);
|
|
+ inno_update_bits(inno, 0xe1, RK3228_TMDS_DRIVER_ENABLE,
|
|
+ RK3228_TMDS_DRIVER_ENABLE);
|
|
+
|
|
+ /* Wait for post PLL lock */
|
|
+ ret = inno_poll(inno, 0xeb, val, val & RK3228_POST_PLL_LOCK_STATUS,
|
|
+ 1000, 10000);
|
|
+ if (ret) {
|
|
+ dev_err(phy->dev, "Post-PLL locking failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (phy_cfg->tmdsclock > 340000000)
|
|
+ mdelay(100);
|
|
+
|
|
+ /* set pdata_en_disable to 0 */
|
|
+ inno_update_bits(inno, 0x02, RK3228_PDATA_EN_DISABLE, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
inno_hdmi_phy_rk3328_power_on(struct phy *phy,
|
|
const struct post_pll_config *cfg,
|
|
@@ -695,6 +1015,18 @@ inno_hdmi_phy_rk3328_power_on(struct phy *phy,
|
|
return 0;
|
|
}
|
|
|
|
+static void inno_hdmi_phy_rk3228_power_off(struct phy *phy)
|
|
+{
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+
|
|
+ debug("%s\n", __func__);
|
|
+
|
|
+ inno_update_bits(inno, 0xe1, RK3228_TMDS_DRIVER_ENABLE, 0);
|
|
+ inno_update_bits(inno, 0xe1, RK3228_BANDGAP_ENABLE, 0);
|
|
+ inno_update_bits(inno, 0xe0, RK3228_POST_PLL_POWER_DOWN,
|
|
+ RK3228_POST_PLL_POWER_DOWN);
|
|
+}
|
|
+
|
|
static void inno_hdmi_phy_rk3328_power_off(struct phy *phy)
|
|
{
|
|
struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
@@ -709,6 +1041,32 @@ static void inno_hdmi_phy_rk3328_power_off(struct phy *phy)
|
|
inno_write(inno, 0x07, 0);
|
|
}
|
|
|
|
+static void inno_hdmi_phy_rk3228_init(struct phy *phy)
|
|
+{
|
|
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
+ const struct inno_hdmi_phy_plat_ops *plat_ops = inno->data->plat_ops;
|
|
+
|
|
+ debug("[PHY] %s: begin!\n", __func__);
|
|
+
|
|
+ /*
|
|
+ * Use phy internal register control
|
|
+ * rxsense/poweron/pllpd/pdataen signal.
|
|
+ */
|
|
+ inno_write(inno, 0x01, RK3228_BYPASS_RXSENSE_EN | RK3228_BYPASS_PWRON_EN | RK3228_BYPASS_PLLPD_EN);
|
|
+ inno_update_bits(inno, 0x02, RK3228_BYPASS_PDATA_EN, RK3228_BYPASS_PDATA_EN);
|
|
+
|
|
+ /* manual power down post-PLL */
|
|
+ inno_update_bits(inno, 0xaa, RK3228_POST_PLL_CTRL_MANUAL, RK3228_POST_PLL_CTRL_MANUAL);
|
|
+
|
|
+ if (plat_ops->clk_recalc_rate)
|
|
+ plat_ops->clk_recalc_rate(phy, clk_get_rate(&inno->refoclk));
|
|
+
|
|
+ if (plat_ops->clk_round_rate)
|
|
+ plat_ops->clk_round_rate(phy, inno->pixclock);
|
|
+
|
|
+ debug("[PHY] %s: done!\n", __func__);
|
|
+}
|
|
+
|
|
static void inno_hdmi_phy_rk3328_init(struct phy *phy)
|
|
{
|
|
struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
|
|
@@ -735,6 +1093,17 @@ static void inno_hdmi_phy_rk3328_init(struct phy *phy)
|
|
plat_ops->clk_round_rate(phy, inno->pixclock);
|
|
}
|
|
|
|
+static const struct inno_hdmi_phy_plat_ops rk3228_hdmi_phy_plat_ops = {
|
|
+ .init = inno_hdmi_phy_rk3228_init,
|
|
+ .power_on = inno_hdmi_phy_rk3228_power_on,
|
|
+ .power_off = inno_hdmi_phy_rk3228_power_off,
|
|
+ .clk_enable = inno_hdmi_phy_rk3228_clk_enable,
|
|
+ .clk_disable = inno_hdmi_phy_rk3228_clk_disable,
|
|
+ .clk_recalc_rate = inno_hdmi_phy_rk3228_clk_recalc_rate,
|
|
+ .clk_round_rate = inno_hdmi_phy_rk3228_clk_round_rate,
|
|
+ .clk_set_rate = inno_hdmi_phy_rk3228_clk_set_rate,
|
|
+};
|
|
+
|
|
static const struct inno_hdmi_phy_plat_ops rk3328_hdmi_phy_plat_ops = {
|
|
.init = inno_hdmi_phy_rk3328_init,
|
|
.power_on = inno_hdmi_phy_rk3328_power_on,
|
|
@@ -861,6 +1230,12 @@ static int inno_hdmi_phy_probe(struct udevice *dev)
|
|
return 0;
|
|
}
|
|
|
|
+static const struct inno_hdmi_phy_data rk3228_inno_hdmi_phy_drv_data = {
|
|
+ .phy_type = INNO_HDMI_PHY_RK3228,
|
|
+ .plat_ops = &rk3228_hdmi_phy_plat_ops,
|
|
+ .phy_cfg_table = rk3228_phy_cfg,
|
|
+};
|
|
+
|
|
static const struct inno_hdmi_phy_data rk3328_inno_hdmi_phy_drv_data = {
|
|
.phy_type = INNO_HDMI_PHY_RK3328,
|
|
.plat_ops = &rk3328_hdmi_phy_plat_ops,
|
|
@@ -868,6 +1243,10 @@ static const struct inno_hdmi_phy_data rk3328_inno_hdmi_phy_drv_data = {
|
|
};
|
|
|
|
static const struct udevice_id inno_hdmi_phy_ids[] = {
|
|
+ {
|
|
+ .compatible = "rockchip,rk3228-hdmi-phy",
|
|
+ .data = (ulong)&rk3228_inno_hdmi_phy_drv_data,
|
|
+ },
|
|
{
|
|
.compatible = "rockchip,rk3328-hdmi-phy",
|
|
.data = (ulong)&rk3328_inno_hdmi_phy_drv_data,
|
|
--
|
|
Armbian
|
|
|