video: rockchip: hdmi: sync to develop-3.10
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rockchip-hdmiv2 / rockchip_hdmiv2_hw.c
index 7b710986b27454682cad3a2bef73d1b753f20088..8d96fc75a0deb721d3090f789eb52c248300cbd0 100755 (executable)
@@ -1,16 +1,19 @@
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/rockchip/cru.h>
 #include <linux/rockchip/grf.h>
 #include <linux/rockchip/iomap.h>
 #include "rockchip_hdmiv2.h"
 #include "rockchip_hdmiv2_hw.h"
 
 static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = {
-       /*tmdsclk = (pixclk / ref_cntrl ) * (fbdiv2 * fbdiv1) / nctrl / tmdsmhl
-         opmode: 0:HDMI1.4     1:HDMI2.0
-       */
-/*     |pixclock|      tmdsclock|pixrepet|colordepth|prepdiv|tmdsmhl|opmode|
-               fbdiv2|fbdiv1|ref_cntrl|nctrl|propctrl|intctrl|gmpctrl| */
+/*     tmdsclk = (pixclk / ref_cntrl ) * (fbdiv2 * fbdiv1) / nctrl / tmdsmhl
+ *     opmode: 0:HDMI1.4       1:HDMI2.0
+ *
+ *     |pixclock|      tmdsclock|pixrepet|colordepth|prepdiv|tmdsmhl|opmode|
+ *             fbdiv2|fbdiv1|ref_cntrl|nctrl|propctrl|intctrl|gmpctrl|
+ */
        {27000000,      27000000,       0,      8,      0,      0,      0,
                2,      3,      0,      3,      3,      0,      0},
        {27000000,      33750000,       0,      10,     1,      0,      0,
@@ -19,8 +22,16 @@ static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = {
                3,      3,      0,      3,      3,      0,      0},
        {27000000,      54000000,       0,      16,     3,      0,      0,
                2,      3,      0,      2,      5,      0,      1},
-/*     {74250000,      74250000,       0,      8,      0,      0,      0,
-       1,      3,      0,      2,      5,      0,      1}, */
+       {59400000,      59400000,       0,      8,      0,      0,      0,
+               1,      3,      0,      2,      5,      0,      1},
+       {59400000,      74250000,       0,      10,     1,      0,      0,
+               5,      0,      0,      2,      5,      0,      1},
+       {59400000,      89100000,       0,      12,     2,      0,      0,
+               2,      2,      0,      2,      5,      0,      1},
+       {59400000,      118800000,      0,      16,     3,      0,      0,
+               1,      3,      0,      1,      7,      0,      2},
+       {65000000,      65000000,       0,      8,      0,      0,      0,
+               1,      3,      0,      2,      5,      0,      1},
        {74250000,      74250000,       0,      8,      0,      0,      0,
                4,      3,      3,      2,      7,      0,      3},
        {74250000,      92812500,       0,      10,     1,      0,      0,
@@ -29,6 +40,16 @@ static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = {
                1,      2,      0,      1,      7,      0,      2},
        {74250000,      148500000,      0,      16,     3,      0,      0,
                1,      3,      0,      1,      7,      0,      2},
+       {83500000,      83500000,       0,      8,      0,      0,      0,
+               1,      3,      0,      2,      5,      0,      1},
+       {85500000,      85500000,       0,      8,      0,      0,      0,
+               1,      3,      0,      2,      5,      0,      1},
+       {106500000,     106500000,      0,      8,      0,      0,      0,
+               1,      1,      0,      1,      7,      0,      2},
+       {108000000,     108000000,      0,      8,      0,      0,      0,
+               1,      1,      0,      1,      7,      0,      2},
+       {146250000,     146250000,      0,      8,      0,      0,      0,
+               1,      1,      0,      1,      7,      0,      2},
        {148500000,     74250000,       0,      8,      0,      0,      0,
                1,      1,      1,      1,      0,      0,      3},
        {148500000,     148500000,      0,      8,      0,      0,      0,
@@ -44,18 +65,65 @@ static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = {
        {297000000,     297000000,      0,      8,      0,      0,      0,
                1,      0,      0,      0,      0,      0,      3},
        {297000000,     371250000,      0,      10,     1,      3,      1,
-               5,      0,      3,      0,      7,      0,      3},
+               5,      1,      3,      1,      7,      0,      3},
        {297000000,     445500000,      0,      12,     2,      3,      1,
-               1,      2,      2,      0,      7,      0,      3},
-       {297000000,     594000000,      0,      16,     1,      3,      1,
+               1,      2,      0,      1,      7,      0,      3},
+       {297000000,     594000000,      0,      16,     3,      3,      1,
                1,      3,      1,      0,      0,      0,      3},
-/*     {594000000,     297000000,      0,      8,      0,      0,      0,
-               1,      3,      3,      1,      0,      0,      3},*/
        {594000000,     297000000,      0,      8,      0,      0,      0,
                1,      0,      1,      0,      0,      0,      3},
+       {594000000,     371250000,      0,      10,     1,      3,      1,
+               5,      0,      3,      0,      7,      0,      3},
+       {594000000,     445500000,      0,      12,     2,      3,      1,
+               1,      2,      1,      1,      7,      0,      3},
+       {594000000,     594000000,      0,      16,     3,      3,      1,
+               1,      3,      3,      0,      0,      0,      3},
        {594000000,     594000000,      0,      8,      0,      3,      1,
                1,      3,      3,      0,      0,      0,      3},
 };
+
+static const struct ext_pll_config_tab EXT_PLL_TABLE[] = {
+       {27000000,      27000000,       8,      1,      90,     3,      2,
+               2,      10,     3,      3,      4,      0,      1,      40,
+               8},
+       {27000000,      33750000,       10,     1,      90,     1,      3,
+               3,      10,     3,      3,      4,      0,      1,      40,
+               8},
+       {59400000,      59400000,       8,      1,      99,     3,      2,
+               2,      1,      3,      3,      4,      0,      1,      40,
+               8},
+       {59400000,      74250000,       10,     1,      99,     1,      2,
+               2,      1,      3,      3,      4,      0,      1,      40,
+               8},
+       {74250000,      74250000,       8,      1,      99,     1,      2,
+               2,      1,      2,      3,      4,      0,      1,      40,
+               8},
+       {74250000,      92812500,       10,     4,      495,    1,      2,
+               2,      1,      3,      3,      4,      0,      2,      40,
+               4},
+       {148500000,     148500000,      8,      1,      99,     1,      1,
+               1,      1,      2,      2,      2,      0,      2,      40,
+               4},
+       {148500000,     185625000,      10,     4,      495,    0,      2,
+               2,      1,      3,      2,      2,      0,      4,      40,
+               2},
+       {297000000,     297000000,      8,      1,      99,     0,      1,
+               1,      1,      0,      2,      2,      0,      4,      40,
+               2},
+       {297000000,     371250000,      10,     4,      495,    1,      2,
+               0,      1,      3,      1,      1,      0,      8,      40,
+               1},
+       {594000000,     297000000,      8,      1,      99,     0,      1,
+               1,      1,      0,      2,      1,      0,      4,      40,
+               2},
+       {594000000,     371250000,      10,     4,      495,    1,      2,
+               0,      1,      3,      1,      1,      1,      8,      40,
+               1},
+       {594000000,     594000000,      8,      1,      99,     0,      2,
+               0,      1,      0,      1,      1,      0,      8,      40,
+               1},
+};
+
 /* ddc i2c master reset */
 static void rockchip_hdmiv2_i2cm_reset(struct hdmi_dev *hdmi_dev)
 {
@@ -146,7 +214,7 @@ static int rockchip_hdmiv2_i2cm_read_data(struct hdmi_dev *hdmi_dev, u8 offset)
 
 static void rockchip_hdmiv2_i2cm_mask_int(struct hdmi_dev *hdmi_dev, int mask)
 {
-       if (0 == mask) {
+       if (!mask) {
                hdmi_msk_reg(hdmi_dev, I2CM_INT,
                             m_I2CM_DONE_MASK, v_I2CM_DONE_MASK(0));
                hdmi_msk_reg(hdmi_dev, I2CM_CTLINT,
@@ -161,7 +229,7 @@ static void rockchip_hdmiv2_i2cm_mask_int(struct hdmi_dev *hdmi_dev, int mask)
        }
 }
 
-#define I2C_DIV_FACTOR 100000
+#define I2C_DIV_FACTOR 1000000
 static u16 i2c_count(u16 sfrclock, u16 sclmintime)
 {
        unsigned long tmp_scl_period = 0;
@@ -177,16 +245,24 @@ static u16 i2c_count(u16 sfrclock, u16 sclmintime)
        return (u16)(tmp_scl_period);
 }
 
-#define EDID_I2C_MIN_SS_SCL_HIGH_TIME  50000
-#define EDID_I2C_MIN_SS_SCL_LOW_TIME   50000
+#define EDID_I2C_MIN_SS_SCL_HIGH_TIME  9625
+#define EDID_I2C_MIN_SS_SCL_LOW_TIME   10000
 
 static void rockchip_hdmiv2_i2cm_clk_init(struct hdmi_dev *hdmi_dev)
 {
+       int value;
+
        /* Set DDC I2C CLK which devided from DDC_CLK. */
+       value = i2c_count(24000, EDID_I2C_MIN_SS_SCL_HIGH_TIME);
        hdmi_writel(hdmi_dev, I2CM_SS_SCL_HCNT_0_ADDR,
-                   i2c_count(24000, EDID_I2C_MIN_SS_SCL_HIGH_TIME));
+                   value & 0xff);
+       hdmi_writel(hdmi_dev, I2CM_SS_SCL_HCNT_1_ADDR,
+                   (value >> 8) & 0xff);
+       value = i2c_count(24000, EDID_I2C_MIN_SS_SCL_LOW_TIME);
        hdmi_writel(hdmi_dev, I2CM_SS_SCL_LCNT_0_ADDR,
-                   i2c_count(24000, EDID_I2C_MIN_SS_SCL_LOW_TIME));
+                   value & 0xff);
+       hdmi_writel(hdmi_dev, I2CM_SS_SCL_LCNT_1_ADDR,
+                   (value >> 8) & 0xff);
        hdmi_msk_reg(hdmi_dev, I2CM_DIV, m_I2CM_FAST_STD_MODE,
                     v_I2CM_FAST_STD_MODE(STANDARD_MODE));
 }
@@ -260,12 +336,28 @@ static void rockchip_hdmiv2_scdc_init(struct hdmi_dev *hdmi_dev)
        rockchip_hdmiv2_i2cm_mask_int(hdmi_dev, 0);/*enable interrupt*/
 }
 
+static void rockchip_hdmiv2_scdc_set_tmds_rate(struct hdmi_dev *hdmi_dev)
+{
+       int stat;
+
+       mutex_lock(&hdmi_dev->ddc_lock);
+       rockchip_hdmiv2_scdc_init(hdmi_dev);
+       stat = rockchip_hdmiv2_i2cm_read_data(hdmi_dev,
+                                             SCDC_TMDS_CONFIG);
+       if (hdmi_dev->tmdsclk > 340000000)
+               stat |= 2;
+       else
+               stat &= 0x1;
+       rockchip_hdmiv2_i2cm_write_data(hdmi_dev,
+                                       stat, SCDC_TMDS_CONFIG);
+       mutex_unlock(&hdmi_dev->ddc_lock);
+}
 
 static int rockchip_hdmiv2_scrambling_enable(struct hdmi_dev *hdmi_dev,
                                             int enable)
 {
        HDMIDBG("%s enable %d\n", __func__, enable);
-       if (1 == enable) {
+       if (enable == 1) {
                /* Write on Rx the bit Scrambling_Enable, register 0x20 */
                rockchip_hdmiv2_i2cm_write_data(hdmi_dev, 1, SCDC_TMDS_CONFIG);
                /* TMDS software reset request */
@@ -287,7 +379,24 @@ static int rockchip_hdmiv2_scrambling_enable(struct hdmi_dev *hdmi_dev,
        return 0;
 }
 
+static const struct ext_pll_config_tab *get_phy_ext_tab(
+               unsigned int pixclock, unsigned int tmdsclk,
+               char colordepth)
+{
+       int i;
 
+       if (pixclock == 0)
+               return NULL;
+       HDMIDBG("%s pixClock %u tmdsclk %u colorDepth %d\n",
+               __func__, pixclock, tmdsclk, colordepth);
+       for (i = 0; i < ARRAY_SIZE(EXT_PLL_TABLE); i++) {
+               if ((EXT_PLL_TABLE[i].pix_clock == pixclock) &&
+                   (EXT_PLL_TABLE[i].tmdsclock == tmdsclk) &&
+                   (EXT_PLL_TABLE[i].color_depth == colordepth))
+                       return &EXT_PLL_TABLE[i];
+       }
+       return NULL;
+}
 
 static const struct phy_mpll_config_tab *get_phy_mpll_tab(
                unsigned int pixclock, unsigned int tmdsclk,
@@ -297,8 +406,8 @@ static const struct phy_mpll_config_tab *get_phy_mpll_tab(
 
        if (pixclock == 0)
                return NULL;
-       HDMIDBG("%s pixClock %u pixRepet %d colorDepth %d\n",
-               __func__, pixclock, pixrepet, colordepth);
+       HDMIDBG("%s pixClock %u tmdsclk %u pixRepet %d colorDepth %d\n",
+               __func__, pixclock, tmdsclk, pixrepet, colordepth);
        for (i = 0; i < ARRAY_SIZE(PHY_MPLL_TABLE); i++) {
                if ((PHY_MPLL_TABLE[i].pix_clock == pixclock) &&
                    (PHY_MPLL_TABLE[i].tmdsclock == tmdsclk) &&
@@ -311,18 +420,33 @@ static const struct phy_mpll_config_tab *get_phy_mpll_tab(
 
 static void rockchip_hdmiv2_powerdown(struct hdmi_dev *hdmi_dev)
 {
-       hdmi_msk_reg(hdmi_dev, PHY_CONF0,
-                    m_PDDQ_SIG | m_TXPWRON_SIG | m_ENHPD_RXSENSE_SIG,
-                    v_PDDQ_SIG(1) | v_TXPWRON_SIG(0) |
-                    v_ENHPD_RXSENSE_SIG(1));
+       hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(1));
+       if (hdmi_dev->soctype != HDMI_SOC_RK322X) {
+               hdmi_msk_reg(hdmi_dev, PHY_CONF0,
+                            m_PDDQ_SIG | m_TXPWRON_SIG |
+                            m_ENHPD_RXSENSE_SIG | m_SVSRET_SIG,
+                            v_PDDQ_SIG(1) | v_TXPWRON_SIG(0) |
+                            v_ENHPD_RXSENSE_SIG(1)) | v_SVSRET_SIG(0);
+       } else {
+               hdmi_msk_reg(hdmi_dev, PHY_CONF0,
+                            m_TXPWRON_SIG | m_ENHPD_RXSENSE_SIG,
+                            v_TXPWRON_SIG(0) | v_ENHPD_RXSENSE_SIG(0));
+               regmap_write(hdmi_dev->grf_base,
+                            RK322X_GRF_SOC_CON2,
+                            RK322X_PLL_PDATA_DEN);
+       }
        hdmi_writel(hdmi_dev, MC_CLKDIS, 0x7f);
 }
 
-static int rockchip_hdmiv2_write_phy(struct hdmi_dev *hdmi_dev,
-                                    int reg_addr, int val)
+int rockchip_hdmiv2_write_phy(struct hdmi_dev *hdmi_dev,
+                             int reg_addr, int val)
 {
        int trytime = 2, i = 0, op_status = 0;
 
+       if (hdmi_dev->phybase) {
+               writel_relaxed(val, hdmi_dev->phybase + (reg_addr) * 0x04);
+               return 0;
+       }
        while (trytime--) {
                hdmi_writel(hdmi_dev, PHY_I2CM_ADDRESS, reg_addr);
                hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_1, (val >> 8) & 0xff);
@@ -342,24 +466,27 @@ static int rockchip_hdmiv2_write_phy(struct hdmi_dev *hdmi_dev,
                                break;
                }
 
-               if (op_status & m_I2CMPHY_DONE)
-                       return 0;
-               else
+               if (!(op_status & m_I2CMPHY_DONE))
                        dev_err(hdmi_dev->hdmi->dev,
                                "[%s] operation error,trytime=%d\n",
                                __func__, trytime);
+               else
+                       return 0;
                msleep(100);
        }
 
        return -1;
 }
 
-static int __maybe_unused rockchip_hdmiv2_read_phy(struct hdmi_dev *hdmi_dev,
-                                                  int reg_addr)
+int rockchip_hdmiv2_read_phy(struct hdmi_dev *hdmi_dev,
+                            int reg_addr)
 {
        int trytime = 2, i = 0, op_status = 0;
        int val = 0;
 
+       if (hdmi_dev->phybase)
+               return readl_relaxed(hdmi_dev->phybase + (reg_addr) * 0x04);
+
        while (trytime--) {
                hdmi_writel(hdmi_dev, PHY_I2CM_ADDRESS, reg_addr);
                hdmi_writel(hdmi_dev, PHY_I2CM_DATAI_1, 0x00);
@@ -378,16 +505,16 @@ static int __maybe_unused rockchip_hdmiv2_read_phy(struct hdmi_dev *hdmi_dev,
                                break;
                }
 
-               if (op_status & m_I2CMPHY_DONE) {
+               if (!(op_status & m_I2CMPHY_DONE)) {
+                       pr_err("[%s] operation error,trytime=%d\n",
+                              __func__, trytime);
+               } else {
                        val = hdmi_readl(hdmi_dev, PHY_I2CM_DATAI_1);
                        val = (val & 0xff) << 8;
                        val += (hdmi_readl(hdmi_dev, PHY_I2CM_DATAI_0) & 0xff);
                        pr_debug("phy_reg0x%02x: 0x%04x",
                                 reg_addr, val);
                        return val;
-               } else {
-                       pr_err("[%s] operation error,trytime=%d\n",
-                              __func__, trytime);
                }
                msleep(100);
        }
@@ -395,13 +522,170 @@ static int __maybe_unused rockchip_hdmiv2_read_phy(struct hdmi_dev *hdmi_dev,
        return -1;
 }
 
-void rockchip_hdmiv2_dump_phy_regs(struct hdmi_dev *hdmi_dev)
+#define PHY_TIMEOUT    10000
+
+static int ext_phy_config(struct hdmi_dev *hdmi_dev)
 {
-       int i;
+       int stat = 0, i = 0, temp;
+       const struct ext_pll_config_tab *phy_ext = NULL;
+
+       if (hdmi_dev->grf_base)
+               regmap_write(hdmi_dev->grf_base,
+                            RK322X_GRF_SOC_CON2,
+                            RK322X_PLL_POWER_DOWN |
+                            RK322X_PLL_PDATA_DEN);
+       if (hdmi_dev->tmdsclk_ratio_change &&
+           hdmi_dev->hdmi->edid.scdc_present == 1)
+               rockchip_hdmiv2_scdc_set_tmds_rate(hdmi_dev);
+
+       /* config the required PHY I2C register */
+       phy_ext = get_phy_ext_tab(hdmi_dev->pixelclk,
+                                 hdmi_dev->tmdsclk,
+                                 hdmi_dev->colordepth);
+       if (phy_ext) {
+               stat = ((phy_ext->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
+                      ((phy_ext->vco_div_5 & 1) << 5) |
+                      (phy_ext->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_PLL_PRE_DIVIDER, stat);
+               stat = phy_ext->pll_nf & 0xff;
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_PLL_FB_DIVIDER, stat);
+               stat = (phy_ext->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
+                      ((phy_ext->pclk_divider_b & 3) << 5);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_PCLK_DIVIDER1, stat);
+               stat = (phy_ext->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
+                      ((phy_ext->pclk_divider_c & 3) << 5);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_PCLK_DIVIDER2, stat);
+               stat = ((phy_ext->tmsd_divider_c & 3) << 4) |
+                      ((phy_ext->tmsd_divider_a & 3) << 2) |
+                      (phy_ext->tmsd_divider_b & 3);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_TMDSCLK_DIVIDER, stat);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_PPLL_FB_DIVIDER,
+                                         phy_ext->ppll_nf);
+
+               if (phy_ext->ppll_no == 1) {
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_PPLL_POST_DIVIDER,
+                                                 0);
+                       stat = 0x20 | phy_ext->ppll_nd;
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_PPLL_PRE_DIVIDER,
+                                                 stat);
+               } else {
+                       stat = ((phy_ext->ppll_no / 2) - 1) << 4;
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_PPLL_POST_DIVIDER,
+                                                 stat);
+                       stat = 0xe0 | phy_ext->ppll_nd;
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_PPLL_PRE_DIVIDER,
+                                                 stat);
+               }
+       } else {
+               pr_err("%s no supported phy configuration.\n", __func__);
+               return -1;
+       }
+
+       if (hdmi_dev->phy_table) {
+               for (i = 0; i < hdmi_dev->phy_table_size; i++) {
+                       temp = hdmi_dev->phy_table[i].maxfreq;
+                       if (hdmi_dev->tmdsclk <= temp)
+                               break;
+               }
+       }
 
-       for (i = 0; i < 0x28; i++)
-               pr_info("phy reg %02x val %04x\n",
-                       i, rockchip_hdmiv2_read_phy(hdmi_dev, i));
+       if (i != hdmi_dev->phy_table_size) {
+               if (hdmi_dev->phy_table[i].slopeboost) {
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_SIGNAL_CTRL, 0xff);
+                       temp = hdmi_dev->phy_table[i].slopeboost - 1;
+                       stat = ((temp & 3) << 6) | ((temp & 3) << 4) |
+                              ((temp & 3) << 2) | (temp & 3);
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_SLOPEBOOST, stat);
+               } else {
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 EXT_PHY_SIGNAL_CTRL, 0x0f);
+               }
+               stat = ((hdmi_dev->phy_table[i].pre_emphasis & 3) << 4) |
+                      ((hdmi_dev->phy_table[i].pre_emphasis & 3) << 2) |
+                      (hdmi_dev->phy_table[i].pre_emphasis & 3);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_PREEMPHASIS, stat);
+               stat = ((hdmi_dev->phy_table[i].clk_level & 0xf) << 4) |
+                      (hdmi_dev->phy_table[i].data2_level & 0xf);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_LEVEL1, stat);
+               stat = ((hdmi_dev->phy_table[i].data1_level & 0xf) << 4) |
+                      (hdmi_dev->phy_table[i].data0_level & 0xf);
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_LEVEL2, stat);
+       } else {
+               rockchip_hdmiv2_write_phy(hdmi_dev,
+                                         EXT_PHY_SIGNAL_CTRL, 0x0f);
+       }
+       rockchip_hdmiv2_write_phy(hdmi_dev, 0xf3, 0x22);
+
+       stat = clk_get_rate(hdmi_dev->pclk_phy) / 100000;
+       rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_CAL,
+                                 ((stat >> 8) & 0xff) | 0x80);
+       rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_CAL_DIV_L,
+                                 stat & 0xff);
+       if (hdmi_dev->tmdsclk > 340000000)
+               stat = EXT_PHY_AUTO_R100_OHMS;
+       else if (hdmi_dev->tmdsclk > 200000000)
+               stat = EXT_PHY_AUTO_R50_OHMS;
+       else
+               stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
+       rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_RESIS_AUTO,
+                                 stat | 0x20);
+       rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_CAL,
+                                 (stat >> 8) & 0xff);
+       if (hdmi_dev->tmdsclk > 200000000)
+               stat = 0;
+       else
+               stat = 0x11;
+       rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PLL_BW, stat);
+       rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_BW, 0x27);
+       if (hdmi_dev->grf_base)
+               regmap_write(hdmi_dev->grf_base,
+                            RK322X_GRF_SOC_CON2,
+                            RK322X_PLL_POWER_UP);
+       if (hdmi_dev->tmdsclk_ratio_change)
+               msleep(100);
+       else
+               usleep_range(900, 1000);
+       hdmi_msk_reg(hdmi_dev, PHY_CONF0,
+                    m_TXPWRON_SIG, v_TXPWRON_SIG(1));
+       i = 0;
+       while (i++ < PHY_TIMEOUT) {
+               if ((i % 10) == 0) {
+                       temp = EXT_PHY_PPLL_POST_DIVIDER;
+                       stat = rockchip_hdmiv2_read_phy(hdmi_dev, temp);
+                       if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
+                               break;
+                       usleep_range(1000, 2000);
+               }
+       }
+       if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
+               stat = hdmi_readl(hdmi_dev, MC_LOCKONCLOCK);
+               dev_err(hdmi_dev->hdmi->dev,
+                       "PHY PLL not locked: PCLK_ON=%d,TMDSCLK_ON=%d\n",
+                       (stat & m_PCLK_ON) >> 6, (stat & m_TMDSCLK_ON) >> 5);
+               return -1;
+       }
+
+       if (hdmi_dev->grf_base)
+               regmap_write(hdmi_dev->grf_base,
+                            RK322X_GRF_SOC_CON2,
+                            RK322X_PLL_PDATA_EN);
+
+       return 0;
 }
 
 static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev)
@@ -409,28 +693,21 @@ static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev)
        int stat = 0, i = 0;
        const struct phy_mpll_config_tab *phy_mpll = NULL;
 
+       if (hdmi_dev->soctype == HDMI_SOC_RK322X)
+               return ext_phy_config(hdmi_dev);
+
        hdmi_msk_reg(hdmi_dev, PHY_I2CM_DIV,
                     m_PHY_I2CM_FAST_STD, v_PHY_I2CM_FAST_STD(0));
+       hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(1));
        /* power off PHY */
-       /* hdmi_writel(hdmi_dev, PHY_CONF0, 0x1e); */
        hdmi_msk_reg(hdmi_dev, PHY_CONF0,
                     m_PDDQ_SIG | m_TXPWRON_SIG | m_SVSRET_SIG,
                     v_PDDQ_SIG(1) | v_TXPWRON_SIG(0) | v_SVSRET_SIG(1));
 
        if (hdmi_dev->tmdsclk_ratio_change &&
-           hdmi_dev->hdmi->edid.scdc_present == 1) {
-               mutex_lock(&hdmi_dev->ddc_lock);
-               rockchip_hdmiv2_scdc_init(hdmi_dev);
-               stat = rockchip_hdmiv2_i2cm_read_data(hdmi_dev,
-                                                     SCDC_TMDS_CONFIG);
-               if (hdmi_dev->tmdsclk > 340000000)
-                       stat |= 2;
-               else
-                       stat &= 0x1;
-               rockchip_hdmiv2_i2cm_write_data(hdmi_dev,
-                                               stat, SCDC_TMDS_CONFIG);
-               mutex_unlock(&hdmi_dev->ddc_lock);
-       }
+           hdmi_dev->hdmi->edid.scdc_present == 1)
+               rockchip_hdmiv2_scdc_set_tmds_rate(hdmi_dev);
+
        /* reset PHY */
        hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(1));
        usleep_range(1000, 2000);
@@ -465,56 +742,58 @@ static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev)
                                          v_MPLL_GMP_CNTRL(
                                          phy_mpll->gmp_cntrl));
        }
-       if (hdmi_dev->tmdsclk <= 74250000) {
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL,
-                                         v_OVERRIDE(1) | v_SLOPEBOOST(0) |
-                                         v_TX_SYMON(1) | v_TX_TRAON(0) |
-                                         v_TX_TRBON(0) | v_CLK_SYMON(1));
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS,
-                                         v_TX_TERM(R100_OHMS));
-       } else if (hdmi_dev->tmdsclk <= 148500000) {
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL,
-                                         v_OVERRIDE(1) | v_SLOPEBOOST(1) |
-                                         v_TX_SYMON(1) | v_TX_TRAON(0) |
-                                         v_TX_TRBON(0) | v_CLK_SYMON(1));
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS,
-                                         v_TX_TERM(R100_OHMS));
-       } else if (hdmi_dev->tmdsclk <= 340000000) {
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL,
-                                         v_OVERRIDE(1) | v_SLOPEBOOST(1) |
-                                         v_TX_SYMON(1) | v_TX_TRAON(0) |
-                                         v_TX_TRBON(0) | v_CLK_SYMON(1));
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS,
-                                         v_TX_TERM(R100_OHMS));
-       } else if (hdmi_dev->tmdsclk > 340000000) {
+
+       if (hdmi_dev->phy_table) {
+               for (i = 0; i < hdmi_dev->phy_table_size; i++)
+                       if (hdmi_dev->tmdsclk <= hdmi_dev->phy_table[i].maxfreq)
+                               break;
+       }
+       if (i == hdmi_dev->phy_table_size) {
+               pr_info("%s use default phy settings\n", __func__);
                rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL,
                                          v_OVERRIDE(1) | v_SLOPEBOOST(0) |
-                                         v_TX_SYMON(1) | v_TX_TRAON(0) |
-                                         v_TX_TRBON(0) | v_CLK_SYMON(1));
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS,
-                                         v_TX_TERM(R100_OHMS));
+                                         v_TX_SYMON(1) | v_CLK_SYMON(1) |
+                                         v_PREEMPHASIS(0));
+               if (hdmi_dev->tmdsclk > 340000000)
+                       rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL,
+                                                 v_SUP_TXLVL(9) |
+                                                 v_SUP_CLKLVL(17));
+               else if (hdmi_dev->tmdsclk > 165000000)
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 PHYTX_VLEVCTRL,
+                                                 v_SUP_TXLVL(14) |
+                                                 v_SUP_CLKLVL(17));
+               else
+                       rockchip_hdmiv2_write_phy(hdmi_dev,
+                                                 PHYTX_VLEVCTRL,
+                                                 v_SUP_TXLVL(18) |
+                                                 v_SUP_CLKLVL(17));
+       } else {
+               stat = v_OVERRIDE(1) | v_TX_SYMON(1) | v_CLK_SYMON(1) |
+                      v_PREEMPHASIS(hdmi_dev->phy_table[i].pre_emphasis) |
+                      v_SLOPEBOOST(hdmi_dev->phy_table[i].slopeboost);
+               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL, stat);
+
+               stat = v_SUP_CLKLVL(hdmi_dev->phy_table[i].clk_level) |
+                      v_SUP_TXLVL(hdmi_dev->phy_table[i].data0_level);
+               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL, stat);
        }
 
-       if (hdmi_dev->tmdsclk < 297000000)
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL,
-                                         v_SUP_TXLVL(18) | v_SUP_CLKLVL(17));
+       if (hdmi_dev->tmdsclk > 340000000)
+               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS,
+                                         v_TX_TERM(R50_OHMS));
        else
-               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL,
-                                         v_SUP_TXLVL(14) | v_SUP_CLKLVL(13));
-
-       rockchip_hdmiv2_write_phy(hdmi_dev, 0x05, 0x8000);
+               rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS,
+                                         v_TX_TERM(R100_OHMS));
+       /* rockchip_hdmiv2_write_phy(hdmi_dev, 0x05, 0x8000); */
        if (hdmi_dev->tmdsclk_ratio_change)
                msleep(100);
        /* power on PHY */
        hdmi_writel(hdmi_dev, PHY_CONF0, 0x2e);
-       /*
-       hdmi_msk_reg(hdmi_dev, PHY_CONF0,
-                    m_PDDQ_SIG | m_TXPWRON_SIG | m_ENHPD_RXSENSE_SIG,
-                    v_PDDQ_SIG(0) | v_TXPWRON_SIG(1) |
-                    v_ENHPD_RXSENSE_SIG(1));
-       */
+
        /* check if the PHY PLL is locked */
-       #define PHY_TIMEOUT     10000
+
+       i = 0;
        while (i++ < PHY_TIMEOUT) {
                if ((i % 10) == 0) {
                        stat = hdmi_readl(hdmi_dev, PHY_STAT0);
@@ -530,7 +809,7 @@ static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev)
                        (stat & m_PCLK_ON) >> 6, (stat & m_TMDSCLK_ON) >> 5);
                return -1;
        }
-
+       hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(0));
        return 0;
 }
 
@@ -562,50 +841,70 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
        mode = &(timing->mode);
        if (vpara->color_input == HDMI_COLOR_YCBCR420)
                tmdsclk = mode->pixclock / 2;
+       else if (vpara->format_3d == HDMI_3D_FRAME_PACKING)
+               tmdsclk = 2 * mode->pixclock;
        else
                tmdsclk = mode->pixclock;
-       switch (vpara->color_output_depth) {
-       case 10:
-               tmdsclk += tmdsclk / 4;
-               break;
-       case 12:
-               tmdsclk += tmdsclk / 2;
-               break;
-       case 16:
-               tmdsclk += tmdsclk;
-               break;
-       case 8:
-       default:
-               break;
+       if (vpara->color_output != HDMI_COLOR_YCBCR422) {
+               switch (vpara->color_output_depth) {
+               case 10:
+                       tmdsclk += tmdsclk / 4;
+                       break;
+               case 12:
+                       tmdsclk += tmdsclk / 2;
+                       break;
+               case 16:
+                       tmdsclk += tmdsclk;
+                       break;
+               case 8:
+               default:
+                       break;
+               }
+       } else if (vpara->color_output_depth > 12) {
+               /* YCbCr422 mode only support up to 12bit */
+               vpara->color_output_depth = 12;
        }
-
-       if (tmdsclk > 594000000) {
+       if ((tmdsclk > 594000000) ||
+           (tmdsclk > 340000000 &&
+            tmdsclk > hdmi_drv->edid.maxtmdsclock)) {
+               pr_warn("out of max tmds clock, limit to 8bit\n");
                vpara->color_output_depth = 8;
-               tmdsclk = mode->pixclock;
+               if (vpara->color_input == HDMI_COLOR_YCBCR420)
+                       tmdsclk = mode->pixclock / 2;
+               else if (vpara->format_3d != HDMI_3D_FRAME_PACKING)
+                       tmdsclk = mode->pixclock;
+               else
+                       return -1;
        }
-       pr_info("pixel clk is %u tmds clk is %u\n", mode->pixclock, tmdsclk);
-       if ((tmdsclk > 340000000 && hdmi_dev->tmdsclk < 340000000) ||
+
+       if ((tmdsclk > 340000000) ||
            (tmdsclk < 340000000 && hdmi_dev->tmdsclk > 340000000))
                hdmi_dev->tmdsclk_ratio_change = true;
        else
                hdmi_dev->tmdsclk_ratio_change = false;
 
        hdmi_dev->tmdsclk = tmdsclk;
-       hdmi_dev->pixelclk = mode->pixclock;
+       if (vpara->format_3d == HDMI_3D_FRAME_PACKING)
+               hdmi_dev->pixelclk = 2 * mode->pixclock;
+       else
+               hdmi_dev->pixelclk = mode->pixclock;
        hdmi_dev->pixelrepeat = timing->pixelrepeat;
-       hdmi_dev->colordepth = vpara->color_output_depth;
-
-       /* Video Register has already been set in uboot,
-          so we no need to set again */
-
-       if (hdmi_drv->uboot)
-               return -1;
-
+       /* hdmi_dev->colordepth is used for find pll config.
+        * For YCbCr422, tmdsclk is same on all color depth.
+        */
+       if (vpara->color_output == HDMI_COLOR_YCBCR422)
+               hdmi_dev->colordepth = 8;
+       else
+               hdmi_dev->colordepth = vpara->color_output_depth;
+       pr_info("pixel clk is %lu tmds clk is %u\n",
+               hdmi_dev->pixelclk, hdmi_dev->tmdsclk);
        /* Start/stop HDCP keepout window generation */
        hdmi_msk_reg(hdmi_dev, FC_INVIDCONF,
                     m_FC_HDCP_KEEPOUT, v_FC_HDCP_KEEPOUT(1));
-       if (hdmi_drv->edid.scdc_present == 1) {
-               if (tmdsclk > 340000000) {/* used for HDMI 2.0 TX */
+       if (hdmi_drv->edid.scdc_present == 1 && !hdmi_drv->uboot) {
+               if (tmdsclk > 340000000 ||
+                   hdmi_drv->edid.lte_340mcsc_scramble) {
+                       /* used for HDMI 2.0 TX */
                        mutex_lock(&hdmi_dev->ddc_lock);
                        rockchip_hdmiv2_scdc_init(hdmi_dev);
                        sink_version =
@@ -624,6 +923,9 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
                        rockchip_hdmiv2_scrambling_enable(hdmi_dev, 0);
                        mutex_unlock(&hdmi_dev->ddc_lock);
                }
+       } else {
+               hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL,
+                            m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(0));
        }
 
        hdmi_msk_reg(hdmi_dev, FC_INVIDCONF,
@@ -632,7 +934,8 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
                     v_FC_VSYNC_POL(vsync_pol) | v_FC_HSYNC_POL(hsync_pol) |
                     v_FC_DE_POL(de_pol) | v_FC_HDMI_DVI(vpara->sink_hdmi) |
                     v_FC_INTERLACE_MODE(mode->vmode));
-       if (mode->vmode == FB_VMODE_INTERLACED)
+       if ((mode->vmode & FB_VMODE_INTERLACED) &&
+           vpara->format_3d != HDMI_3D_FRAME_PACKING)
                hdmi_msk_reg(hdmi_dev, FC_INVIDCONF,
                             m_FC_VBLANK, v_FC_VBLANK(1));
        else
@@ -645,7 +948,20 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
        hdmi_writel(hdmi_dev, FC_INHACTIV1, v_FC_HACTIVE1(value >> 8));
        hdmi_writel(hdmi_dev, FC_INHACTIV0, (value & 0xff));
 
-       value = mode->yres;
+       if (vpara->format_3d == HDMI_3D_FRAME_PACKING) {
+               if (mode->vmode == 0)
+                       value = 2 * mode->yres +
+                               mode->upper_margin +
+                               mode->lower_margin +
+                               mode->vsync_len;
+               else
+                       value = 2 * mode->yres +
+                               3 * (mode->upper_margin +
+                                    mode->lower_margin +
+                                    mode->vsync_len) + 2;
+       } else {
+               value = mode->yres;
+       }
        hdmi_writel(hdmi_dev, FC_INVACTIV1, v_FC_VACTIVE1(value >> 8));
        hdmi_writel(hdmi_dev, FC_INVACTIV0, (value & 0xff));
 
@@ -676,19 +992,21 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
        value = mode->vsync_len;
        hdmi_writel(hdmi_dev, FC_VSYNCINWIDTH, (value & 0xff));
 
-       /*Set the control period minimum duration
-        (min. of 12 pixel clock cycles, refer to HDMI 1.4b specification)*/
+       /* Set the control period minimum duration (min. of 12 pixel
+        * clock cycles, refer to HDMI 1.4b specification)
+        */
        hdmi_writel(hdmi_dev, FC_CTRLDUR, 12);
        hdmi_writel(hdmi_dev, FC_EXCTRLDUR, 32);
 
-       hdmi_writel(hdmi_dev, FC_EXCTRLSPAC,
-                   (hdmi_dev->tmdsclk/1000) * 50 / (256 * 512));
-
-#if 0
        /* spacing < 256^2 * config / tmdsClock, spacing <= 50ms
         * worst case: tmdsClock == 25MHz => config <= 19
         */
-       hdmi_writel(hdmi_dev, FC_EXCTRLSPAC, 1);
+       hdmi_writel(hdmi_dev, FC_EXCTRLSPAC,
+                   (hdmi_dev->tmdsclk/1000) * 50 / (256 * 512));
+#if 0
+       if (!hdmi_drv->uboot)
+               hdmi_msk_reg(hdmi_dev, MC_SWRSTZREQ,
+                            m_TMDS_SWRST, v_TMDS_SWRST(0));
 
        /*Set PreambleFilter*/
        for (i = 0; i < 3; i++) {
@@ -702,7 +1020,9 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
        }
 #endif
 
-       hdmi_writel(hdmi_dev, FC_PRCONF, v_FC_PR_FACTOR(timing->pixelrepeat));
+       hdmi_writel(hdmi_dev, FC_PRCONF,
+                   v_FC_PR_FACTOR(timing->pixelrepeat) |
+                   v_FC_PR_FACTOR_OUT(timing->pixelrepeat - 1));
 
        return 0;
 }
@@ -710,7 +1030,7 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
 static int rockchip_hdmiv2_video_packetizer(struct hdmi_dev *hdmi_dev,
                                            struct hdmi_video *vpara)
 {
-       unsigned char color_depth = 0;
+       unsigned char color_depth = COLOR_DEPTH_24BIT_DEFAULT;
        unsigned char output_select = 0;
        unsigned char remap_size = 0;
 
@@ -754,12 +1074,10 @@ static int rockchip_hdmiv2_video_packetizer(struct hdmi_dev *hdmi_dev,
                        output_select = OUT_FROM_8BIT_BYPASS;
                        break;
                }
-
-               /*Config Color Depth*/
-               hdmi_msk_reg(hdmi_dev, VP_PR_CD,
-                            m_COLOR_DEPTH, v_COLOR_DEPTH(color_depth));
        }
-
+       /*Config Color Depth*/
+       hdmi_msk_reg(hdmi_dev, VP_PR_CD,
+                    m_COLOR_DEPTH, v_COLOR_DEPTH(color_depth));
        /*Config pixel repettion*/
        hdmi_msk_reg(hdmi_dev, VP_PR_CD, m_DESIRED_PR_FACTOR,
                     v_DESIRED_PR_FACTOR(hdmi_dev->pixelrepeat - 1));
@@ -863,7 +1181,8 @@ static int rockchip_hdmiv2_video_sampler(struct hdmi_dev *hdmi_dev,
        }
 
        /* Set Data enable signal from external
-          and set video sample input mapping */
+        * and set video sample input mapping
+        */
        hdmi_msk_reg(hdmi_dev, TX_INVID0,
                     m_INTERNAL_DE_GEN | m_VIDEO_MAPPING,
                     v_INTERNAL_DE_GEN(0) | v_VIDEO_MAPPING(map_code));
@@ -887,9 +1206,10 @@ static int rockchip_hdmiv2_video_sampler(struct hdmi_dev *hdmi_dev,
 
 static const char coeff_csc[][24] = {
                /*   G          R           B           Bias
-                    A1    |    A2     |    A3     |    A4    |
-                    B1    |    B2     |    B3     |    B4    |
-                    C1    |    C2     |    C3     |    C4    | */
+                *   A1    |    A2     |    A3     |    A4    |
+                *   B1    |    B2     |    B3     |    B4    |
+                *   C1    |    C2     |    C3     |    C4    |
+                */
        {       /* CSC_RGB_0_255_TO_RGB_16_235_8BIT */
                0x36, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,         /*G*/
                0x00, 0x00, 0x36, 0xf7, 0x00, 0x00, 0x00, 0x40,         /*R*/
@@ -1040,14 +1360,14 @@ static int rockchip_hdmiv2_video_csc(struct hdmi_dev *hdmi_dev,
 static int hdmi_dev_detect_hotplug(struct hdmi *hdmi)
 {
        struct hdmi_dev *hdmi_dev = hdmi->property->priv;
-       u32 value = hdmi_readl(hdmi_dev, PHY_STAT0);
+       u32 value;
 
+       value = hdmi_readl(hdmi_dev, PHY_STAT0);
        HDMIDBG("[%s] reg%x value %02x\n", __func__, PHY_STAT0, value);
-
        if (value & m_PHY_HPD)
                return HDMI_HPD_ACTIVED;
-       else
-               return HDMI_HPD_REMOVED;
+
+       return HDMI_HPD_REMOVED;
 }
 
 static int hdmi_dev_read_edid(struct hdmi *hdmi, int block, unsigned char *buff)
@@ -1125,16 +1445,6 @@ static int hdmi_dev_read_edid(struct hdmi *hdmi, int block, unsigned char *buff)
 exit:
        /* Disable I2C interrupt */
        rockchip_hdmiv2_i2cm_mask_int(hdmi_dev, 1);
-
-       #ifdef DEBUG
-       if (!ret) {
-               for (index = 0; index < 128; index++) {
-                       printk("0x%02x ,", buff[index]);
-                       if ((index + 1) % 16 == 0)
-                               printk("\n");
-               }
-       }
-       #endif
        return ret;
 }
 
@@ -1144,6 +1454,9 @@ static void hdmi_dev_config_avi(struct hdmi_dev *hdmi_dev,
        unsigned char colorimetry, ext_colorimetry, aspect_ratio, y1y0;
        unsigned char rgb_quan_range = AVI_QUANTIZATION_RANGE_DEFAULT;
 
+       hdmi_msk_reg(hdmi_dev, FC_DATAUTO3, m_AVI_AUTO, v_AVI_AUTO(0));
+       hdmi_msk_reg(hdmi_dev, IH_FC_STAT1,
+                    m_AVI_INFOFRAME, v_AVI_INFOFRAME(1));
        /* Set AVI infoFrame Data byte1 */
        if (vpara->color_output == HDMI_COLOR_YCBCR444)
                y1y0 = AVI_COLOR_MODE_YCBCR444;
@@ -1165,27 +1478,32 @@ static void hdmi_dev_config_avi(struct hdmi_dev *hdmi_dev,
        case HDMI_720X480P_60HZ_4_3:
        case HDMI_720X576P_50HZ_4_3:
                aspect_ratio = AVI_CODED_FRAME_ASPECT_4_3;
-               colorimetry = AVI_COLORIMETRY_SMPTE_170M;
+               if (vpara->colorimetry == HDMI_COLORIMETRY_NO_DATA)
+                       colorimetry = AVI_COLORIMETRY_SMPTE_170M;
                break;
        case HDMI_720X480I_60HZ_16_9:
        case HDMI_720X576I_50HZ_16_9:
        case HDMI_720X480P_60HZ_16_9:
        case HDMI_720X576P_50HZ_16_9:
                aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9;
-               colorimetry = AVI_COLORIMETRY_SMPTE_170M;
+               if (vpara->colorimetry == HDMI_COLORIMETRY_NO_DATA)
+                       colorimetry = AVI_COLORIMETRY_SMPTE_170M;
                break;
        default:
                aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9;
-               colorimetry = AVI_COLORIMETRY_ITU709;
+               if (vpara->colorimetry == HDMI_COLORIMETRY_NO_DATA)
+                       colorimetry = AVI_COLORIMETRY_ITU709;
        }
 
-       if (vpara->color_output_depth > 8) {
+       if (vpara->colorimetry > HDMI_COLORIMETRY_ITU709) {
                colorimetry = AVI_COLORIMETRY_EXTENDED;
-               ext_colorimetry = 6;
+               ext_colorimetry = vpara->colorimetry;
        } else if (vpara->color_output == HDMI_COLOR_RGB_16_235 ||
                 vpara->color_output == HDMI_COLOR_RGB_0_255) {
                colorimetry = AVI_COLORIMETRY_NO_DATA;
                ext_colorimetry = 0;
+       } else if (vpara->colorimetry != HDMI_COLORIMETRY_NO_DATA) {
+               colorimetry = vpara->colorimetry;
        }
 
        hdmi_writel(hdmi_dev, FC_AVICONF1,
@@ -1200,13 +1518,16 @@ static void hdmi_dev_config_avi(struct hdmi_dev *hdmi_dev,
                     v_FC_QUAN_RANGE(rgb_quan_range));
 
        /* Set AVI infoFrame Data byte4 */
-       if ((vpara->vic > 92 && vpara->vic < 96) || (vpara->vic == 98))
+       if ((vpara->vic > 92 && vpara->vic < 96) ||
+           (vpara->vic == 98) ||
+           (vpara->vic & HDMI_VIDEO_DMT))
                hdmi_writel(hdmi_dev, FC_AVIVID, 0);
        else
                hdmi_writel(hdmi_dev, FC_AVIVID, vpara->vic & 0xff);
        /* Set AVI infoFrame Data byte5 */
        hdmi_msk_reg(hdmi_dev, FC_AVICONF3, m_FC_YQ | m_FC_CN,
                     v_FC_YQ(YQ_LIMITED_RANGE) | v_FC_CN(CN_GRAPHICS));
+       hdmi_msk_reg(hdmi_dev, FC_DATAUTO3, m_AVI_AUTO, v_AVI_AUTO(1));
 }
 
 static int hdmi_dev_config_vsi(struct hdmi *hdmi,
@@ -1243,15 +1564,44 @@ static int hdmi_dev_config_vsi(struct hdmi *hdmi,
        for (i = 0; i < 3; i++)
                hdmi_writel(hdmi_dev, FC_VSDPAYLOAD0 + i, data[i]);
        hdmi_writel(hdmi_dev, FC_VSDSIZE, 0x6);
-/*     if (auto_send) { */
+
        hdmi_writel(hdmi_dev, FC_DATAUTO1, 0);
        hdmi_writel(hdmi_dev, FC_DATAUTO2, 0x11);
        hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_VSD_AUTO, v_VSD_AUTO(1));
-/*     }
-       else {
-               hdmi_msk_reg(hdmi_dev, FC_DATMAN, m_VSD_MAN, v_VSD_MAN(1));
+       return 0;
+}
+
+static int hdmi_dev_config_spd(struct hdmi *hdmi, const char *vendor,
+                              const char *product, char deviceinfo)
+{
+       struct hdmi_dev *hdmi_dev;
+       int i, len;
+
+       if (!hdmi || !vendor || !product)
+               return -1;
+       hdmi_dev = hdmi->property->priv;
+
+       hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_SPD_AUTO, v_SPD_AUTO(0));
+       len = strlen(vendor);
+       for (i = 0; i < 8; i++) {
+               if (i < len)
+                       hdmi_writel(hdmi_dev, FC_SPDVENDORNAME0 + i,
+                                   vendor[i]);
+               else
+                       hdmi_writel(hdmi_dev, FC_SPDVENDORNAME0 + i,
+                                   0);
        }
-*/
+       len = strlen(product);
+       for (i = 0; i < 16; i++) {
+               if (i < len)
+                       hdmi_writel(hdmi_dev, FC_SPDPRODUCTNAME0 + i,
+                                   product[i]);
+               else
+                       hdmi_writel(hdmi_dev, FC_SPDPRODUCTNAME0 + i,
+                                   0);
+       }
+       hdmi_writel(hdmi_dev, FC_SPDDEVICEINF, deviceinfo);
+       hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_SPD_AUTO, v_SPD_AUTO(1));
        return 0;
 }
 
@@ -1268,10 +1618,18 @@ static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara)
 
        if (!hdmi->uboot) {
                /* befor configure video, we power off phy */
-               hdmi_msk_reg(hdmi_dev, PHY_CONF0,
-                            m_PDDQ_SIG | m_TXPWRON_SIG,
-                            v_PDDQ_SIG(1) | v_TXPWRON_SIG(0));
-
+               if (hdmi_dev->soctype != HDMI_SOC_RK322X) {
+                       hdmi_msk_reg(hdmi_dev, PHY_CONF0,
+                                    m_PDDQ_SIG | m_TXPWRON_SIG,
+                                    v_PDDQ_SIG(1) | v_TXPWRON_SIG(0));
+               } else {
+                       hdmi_msk_reg(hdmi_dev, PHY_CONF0,
+                                    m_ENHPD_RXSENSE_SIG,
+                                    v_ENHPD_RXSENSE_SIG(1));
+                       regmap_write(hdmi_dev->grf_base,
+                                    RK322X_GRF_SOC_CON2,
+                                    RK322X_PLL_POWER_DOWN);
+               }
                /* force output blue */
                if (vpara->color_output == HDMI_COLOR_RGB_0_255) {
                        hdmi_writel(hdmi_dev, FC_DBGTMDS2, 0x00);       /*R*/
@@ -1282,9 +1640,9 @@ static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara)
                        hdmi_writel(hdmi_dev, FC_DBGTMDS1, 0x10);       /*G*/
                        hdmi_writel(hdmi_dev, FC_DBGTMDS0, 0x10);       /*B*/
                } else {
-                       hdmi_writel(hdmi_dev, FC_DBGTMDS2, 0x80);       /*R*/
-                       hdmi_writel(hdmi_dev, FC_DBGTMDS1, 0x10);       /*G*/
-                       hdmi_writel(hdmi_dev, FC_DBGTMDS0, 0x80);       /*B*/
+                       hdmi_writel(hdmi_dev, FC_DBGTMDS2, 0x80);       /*Cr*/
+                       hdmi_writel(hdmi_dev, FC_DBGTMDS1, 0x10);       /*Y*/
+                       hdmi_writel(hdmi_dev, FC_DBGTMDS0, 0x80);       /*Cb*/
                }
                hdmi_msk_reg(hdmi_dev, FC_DBGFORCE,
                             m_FC_FORCEVIDEO, v_FC_FORCEVIDEO(1));
@@ -1304,6 +1662,9 @@ static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara)
 
        if (vpara->sink_hdmi == OUTPUT_HDMI) {
                hdmi_dev_config_avi(hdmi_dev, vpara);
+               hdmi_dev_config_spd(hdmi, hdmi_dev->vendor_name,
+                                   hdmi_dev->product_name,
+                                   hdmi_dev->deviceinfo);
                if (vpara->format_3d != HDMI_3D_NONE) {
                        hdmi_dev_config_vsi(hdmi,
                                            vpara->format_3d,
@@ -1325,30 +1686,35 @@ static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara)
                dev_info(hdmi->dev, "[%s] sucess output DVI.\n", __func__);
        }
 
-       rockchip_hdmiv2_config_phy(hdmi_dev);
+       if (!hdmi->uboot)
+               rockchip_hdmiv2_config_phy(hdmi_dev);
+       else
+               hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(0));
        return 0;
 }
 
 static void hdmi_dev_config_aai(struct hdmi_dev *hdmi_dev,
                                struct hdmi_audio *audio)
 {
-       /*Refer to CEA861-E Audio infoFrame*/
-       /*Set both Audio Channel Count and Audio Coding
-         Type Refer to Stream Head for HDMI*/
+       /* Refer to CEA861-E Audio infoFrame
+        * Set both Audio Channel Count and Audio Coding
+        * Type Refer to Stream Head for HDMI
+        */
        hdmi_msk_reg(hdmi_dev, FC_AUDICONF0,
                     m_FC_CHN_CNT | m_FC_CODING_TYEP,
                     v_FC_CHN_CNT(audio->channel-1) | v_FC_CODING_TYEP(0));
 
-       /*Set both Audio Sample Size and Sample Frequency
-         Refer to Stream Head for HDMI*/
+       /* Set both Audio Sample Size and Sample Frequency
+        * Refer to Stream Head for HDMI
+        */
        hdmi_msk_reg(hdmi_dev, FC_AUDICONF1,
                     m_FC_SAMPLE_SIZE | m_FC_SAMPLE_FREQ,
                     v_FC_SAMPLE_SIZE(0) | v_FC_SAMPLE_FREQ(0));
 
-       /*Set Channel Allocation*/
+       /* Set Channel Allocation */
        hdmi_writel(hdmi_dev, FC_AUDICONF2, 0x00);
 
-       /*Set LFEPBL¡¢DOWN-MIX INH and LSV*/
+       /* Set LFEPBLDOWN-MIX INH and LSV */
        hdmi_writel(hdmi_dev, FC_AUDICONF3, 0x00);
 }
 
@@ -1507,12 +1873,12 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                hdmi_writel(hdmi_dev, MC_SWRSTZREQ, 0xF7);
                hdmi_writel(hdmi_dev, AUD_CONF2, 0x0);
                usleep_range(90, 100);
-               if (I2S_CHANNEL_7_8 == channel) {
+               if (channel == I2S_CHANNEL_7_8) {
                        HDMIDBG("hbr mode.\n");
                        hdmi_writel(hdmi_dev, AUD_CONF2, 0x1);
                        word_length = I2S_24BIT_SAMPLE;
-               } else if ((HDMI_AUDIO_FS_48000 == audio->rate) ||
-                          (HDMI_AUDIO_FS_192000 == audio->rate)) {
+               } else if ((audio->rate == HDMI_AUDIO_FS_48000) ||
+                          (audio->rate == HDMI_AUDIO_FS_192000)) {
                        HDMIDBG("nlpcm mode.\n");
                        hdmi_writel(hdmi_dev, AUD_CONF2, 0x2);
                        word_length = I2S_24BIT_SAMPLE;
@@ -1561,37 +1927,45 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
 static int hdmi_dev_control_output(struct hdmi *hdmi, int enable)
 {
        struct hdmi_dev *hdmi_dev = hdmi->property->priv;
+       struct hdmi_video vpara;
 
        HDMIDBG("[%s] %d\n", __func__, enable);
-
        if (enable == HDMI_AV_UNMUTE) {
                hdmi_writel(hdmi_dev, FC_DBGFORCE, 0x00);
-               hdmi_msk_reg(hdmi_dev, FC_GCP,
-                            m_FC_SET_AVMUTE | m_FC_CLR_AVMUTE,
-                            v_FC_SET_AVMUTE(0) | v_FC_CLR_AVMUTE(1));
+               if (hdmi->edid.sink_hdmi == OUTPUT_HDMI)
+                       hdmi_msk_reg(hdmi_dev, FC_GCP,
+                                    m_FC_SET_AVMUTE | m_FC_CLR_AVMUTE,
+                                    v_FC_SET_AVMUTE(0) | v_FC_CLR_AVMUTE(1));
        } else {
                if (enable & HDMI_VIDEO_MUTE) {
                        hdmi_msk_reg(hdmi_dev, FC_DBGFORCE,
                                     m_FC_FORCEVIDEO, v_FC_FORCEVIDEO(1));
-                       hdmi_msk_reg(hdmi_dev, FC_GCP,
-                                    m_FC_SET_AVMUTE | m_FC_CLR_AVMUTE,
-                                    v_FC_SET_AVMUTE(1) | v_FC_CLR_AVMUTE(0));
+                       if (hdmi->edid.sink_hdmi == OUTPUT_HDMI) {
+                               hdmi_msk_reg(hdmi_dev, FC_GCP,
+                                            m_FC_SET_AVMUTE |
+                                            m_FC_CLR_AVMUTE,
+                                            v_FC_SET_AVMUTE(1) |
+                                            v_FC_CLR_AVMUTE(0));
+                               vpara.vic = hdmi->vic;
+                               vpara.color_output = HDMI_COLOR_RGB_0_255;
+                               hdmi_dev_config_avi(hdmi_dev, &vpara);
+                               while ((!hdmi_readl(hdmi_dev, IH_FC_STAT1)) &
+                                      m_AVI_INFOFRAME) {
+                                       usleep_range(900, 1000);
+                               }
+                       }
                }
 /*             if (enable & HDMI_AUDIO_MUTE) {
-                       hdmi_msk_reg(hdmi_dev, FC_AUDSCONF,
-                                    m_AUD_PACK_SAMPFIT,
-                                    v_AUD_PACK_SAMPFIT(0x0F));
-               }
-*/             if (enable == (HDMI_VIDEO_MUTE | HDMI_AUDIO_MUTE)) {
-                       msleep(100);
+ *                     hdmi_msk_reg(hdmi_dev, FC_AUDSCONF,
+ *                                  m_AUD_PACK_SAMPFIT,
+ *                                  v_AUD_PACK_SAMPFIT(0x0F));
+ *             }
+ */
+               if (enable == (HDMI_VIDEO_MUTE | HDMI_AUDIO_MUTE)) {
+                       if (hdmi->ops->hdcp_power_off_cb)
+                               hdmi->ops->hdcp_power_off_cb(hdmi);
                        rockchip_hdmiv2_powerdown(hdmi_dev);
-                       hdmi_dev->tmdsclk = 0;
-/*
-                       hdmi_msk_reg(hdmi_dev, PHY_CONF0,
-                                    m_PDDQ_SIG | m_TXPWRON_SIG,
-                                    v_PDDQ_SIG(1) | v_TXPWRON_SIG(0));
-                       hdmi_writel(hdmi_dev, MC_CLKDIS, 0x7f);
-*/             }
+               }
        }
        return 0;
 }
@@ -1611,6 +1985,8 @@ static int hdmi_dev_remove(struct hdmi *hdmi)
        struct hdmi_dev *hdmi_dev = hdmi->property->priv;
 
        HDMIDBG("%s\n", __func__);
+       if (hdmi->ops->hdcp_power_off_cb)
+               hdmi->ops->hdcp_power_off_cb(hdmi);
        rockchip_hdmiv2_powerdown(hdmi_dev);
        hdmi_dev->tmdsclk = 0;
        return HDMI_ERROR_SUCESS;
@@ -1625,7 +2001,7 @@ static int hdmi_dev_enable(struct hdmi *hdmi)
                hdmi_writel(hdmi_dev, IH_MUTE, 0x00);
                hdmi_dev->enable = 1;
        }
-       hdmi_submit_work(hdmi, HDMI_HPD_CHANGE, 10, NULL);
+       hdmi_submit_work(hdmi, HDMI_HPD_CHANGE, 10, 0);
        return 0;
 }
 
@@ -1662,29 +2038,36 @@ void rockchip_hdmiv2_dev_initial(struct hdmi_dev *hdmi_dev)
        struct hdmi *hdmi = hdmi_dev->hdmi;
 
        if (!hdmi->uboot) {
-               /* reset hdmi */
+               pr_info("reset hdmi\n");
                if (hdmi_dev->soctype == HDMI_SOC_RK3288) {
-                       writel_relaxed((1 << 9) | (1 << 25),
-                                      RK_CRU_VIRT + 0x01d4);
-                       udelay(1);
-                       writel_relaxed((0 << 9) | (1 << 25),
-                                      RK_CRU_VIRT + 0x01d4);
-               } else if (hdmi_dev->soctype == HDMI_SOC_RK3368) {
-                       pr_info("reset hdmi\n");
-                       regmap_write(hdmi_dev->grf_base, 0x031c,
-                                    (1 << 9) | (1 << 25));
-                       udelay(5);
-                       regmap_write(hdmi_dev->grf_base, 0x031c,
-                                    (0 << 9) | (1 << 25));
+                       rk3288_cru_set_soft_reset(RK3288_SOFT_RST_HDMI, true);
+                       usleep_range(10, 20);
+                       rk3288_cru_set_soft_reset(RK3288_SOFT_RST_HDMI, false);
+               } else {
+                       if (hdmi_dev->soctype == HDMI_SOC_RK322X) {
+                               regmap_write(hdmi_dev->grf_base,
+                                            RK322X_GRF_SOC_CON2,
+                                            RK322X_DDC_MASK_EN);
+                               regmap_write(hdmi_dev->grf_base,
+                                            RK322X_GRF_SOC_CON6,
+                                            RK322X_IO_3V_DOMAIN);
+                       }
+                       reset_control_assert(hdmi_dev->reset);
+                       usleep_range(10, 20);
+                       reset_control_deassert(hdmi_dev->reset);
                }
                rockchip_hdmiv2_powerdown(hdmi_dev);
+       } else {
+               hdmi->hotplug = hdmi_dev_detect_hotplug(hdmi);
+               if (hdmi->hotplug != HDMI_HPD_ACTIVED)
+                       hdmi->uboot = 0;
        }
        /*mute unnecessary interrrupt, only enable hpd*/
        hdmi_writel(hdmi_dev, IH_MUTE_FC_STAT0, 0xff);
        hdmi_writel(hdmi_dev, IH_MUTE_FC_STAT1, 0xff);
        hdmi_writel(hdmi_dev, IH_MUTE_FC_STAT2, 0xff);
        hdmi_writel(hdmi_dev, IH_MUTE_AS_STAT0, 0xff);
-       hdmi_writel(hdmi_dev, IH_MUTE_PHY_STAT0, 0xfe);
+       hdmi_writel(hdmi_dev, IH_MUTE_PHY_STAT0, 0xfc);
        hdmi_writel(hdmi_dev, IH_MUTE_I2CM_STAT0, 0xff);
        hdmi_writel(hdmi_dev, IH_MUTE_CEC_STAT0, 0xff);
        hdmi_writel(hdmi_dev, IH_MUTE_VP_STAT0, 0xff);
@@ -1726,11 +2109,16 @@ irqreturn_t rockchip_hdmiv2_dev_irq(int irq, void *priv)
        hdmi_writel(hdmi_dev, IH_VP_STAT0, vp_stat0);
 
        if (phy_int0 || phy_int) {
+               if ((phy_int0 & m_PHY_LOCK) &&
+                   (phy_pol & m_PHY_LOCK) == 0) {
+                       pr_info("hdmi phy pll unlock\n");
+                       hdmi_submit_work(hdmi, HDMI_SET_VIDEO, 0, 0);
+               }
                phy_pol = (phy_int0 & (~phy_status)) | ((~phy_int0) & phy_pol);
                hdmi_writel(hdmi_dev, PHY_POL0, phy_pol);
                hdmi_writel(hdmi_dev, IH_PHY_STAT0, phy_int);
                if ((phy_int & m_HPD) || ((phy_int & 0x3c) == 0x3c))
-                       hdmi_submit_work(hdmi, HDMI_HPD_CHANGE, 20, NULL);
+                       hdmi_submit_work(hdmi, HDMI_HPD_CHANGE, 20, 0);
        }
 
        /* Audio error */
@@ -1743,12 +2131,13 @@ irqreturn_t rockchip_hdmiv2_dev_irq(int irq, void *priv)
        /* CEC */
        if (cec_int) {
                hdmi_writel(hdmi_dev, IH_CEC_STAT0, cec_int);
-               rockchip_hdmiv2_cec_isr(hdmi_dev, cec_int);
+               if (hdmi_dev->hdmi->property->feature & SUPPORT_CEC)
+                       rockchip_hdmiv2_cec_isr(hdmi_dev, cec_int);
        }
        /* HDCP */
        if (hdcp_int) {
                hdmi_writel(hdmi_dev, A_APIINTCLR, hdcp_int);
-               pr_info("hdcp_int is 0x%02x\n", hdcp_int);
+               rockchip_hdmiv2_hdcp_isr(hdmi_dev, hdcp_int);
        }
 
        /* HDCP2 */