video: rockchip: add auto dp support for vr product
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rockchip-hdmiv2 / rockchip_hdmiv2_hw.c
index b0295dd5f47c93e2a2217357ad168062e36ea14d..f38a1d8ce6e2d2919197788ccabb964d75aed78f 100644 (file)
@@ -1,26 +1,46 @@
+#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"
+#include <linux/rockchip/grf.h>
+
+#define HDMI_SEL_LCDC(x, bit)  ((((x) & 1) << bit) | (1 << (16 + bit)))
+#define grf_writel(v, offset)  writel_relaxed(v, RK_GRF_VIRT + offset)
+#define RK3399_GRF_SOC_CON20 0x6250
 
 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,      27000000,       1,      8,      0,      0,      0,
+               2,      3,      0,      3,      3,      0,      0},
        {27000000,      33750000,       0,      10,     1,      0,      0,
                5,      1,      0,      3,      3,      0,      0},
+       {27000000,      33750000,       1,      10,     1,      0,      0,
+               5,      1,      0,      3,      3,      0,      0},
        {27000000,      40500000,       0,      12,     2,      0,      0,
                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 +49,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,
@@ -39,23 +69,82 @@ static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = {
                1,      2,      1,      0,      7,      0,      3},
        {148500000,     297000000,      0,      16,     3,      0,      0,
                1,      1,      0,      0,      7,      0,      3},
+       {148500000,     297000000,      0,      8,      0,      0,      0,
+               1,      1,      0,      0,      0,      0,      3},
+       {148500000,     594000000,      0,      8,      0,      3,      1,
+               1,      3,      0,      0,      0,      0,      3},
+       {269390000,     269390000,      0,      8,      0,      0,      0,
+               1,      0,      0,      0,      0,      0,      3},
+       {285000000,     285000000,      0,      8,      0,      0,      0,
+               1,      0,      0,      0,      0,      0,      3},
        {297000000,     148500000,      0,      8,      0,      0,      0,
                1,      0,      1,      0,      0,      0,      3},
        {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},*/
+       {340000000,     340000000,      0,      8,      0,      0,      0,
+               1,      0,      0,      0,      0,      0,      3},
+       {403000000,     403000000,      0,      8,      0,      3,      1,
+               1,      3,      3,      0,      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,      1,      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)
 {
@@ -83,7 +172,7 @@ static void rockchip_hdmiv2_i2cm_read_request(struct hdmi_dev *hdmi_dev,
 static void rockchip_hdmiv2_i2cm_write_data(struct hdmi_dev *hdmi_dev,
                                            u8 data, u8 offset)
 {
-       u8 interrupt;
+       u8 interrupt = 0;
        int trytime = 2;
        int i = 20;
 
@@ -116,7 +205,7 @@ static void rockchip_hdmiv2_i2cm_write_data(struct hdmi_dev *hdmi_dev,
 
 static int rockchip_hdmiv2_i2cm_read_data(struct hdmi_dev *hdmi_dev, u8 offset)
 {
-       u8 interrupt, val;
+       u8 interrupt = 0, val;
        int trytime = 2;
        int i = 20;
 
@@ -146,7 +235,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 +250,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 +266,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)
 {
-       /* Set DDC I2C CLK which devided from DDC_CLK. */
+       int value;
+
+       /* Set DDC I2C CLK which divided 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));
 }
@@ -202,7 +299,6 @@ static void rockchip_hdmiv2_scdc_set_source_version(struct hdmi_dev *hdmi_dev,
        rockchip_hdmiv2_i2cm_write_data(hdmi_dev, version, SCDC_SOURCE_VER);
 }
 
-
 static void rockchip_hdmiv2_scdc_read_request(struct hdmi_dev *hdmi_dev,
                                              int enable)
 {
@@ -218,7 +314,6 @@ static void rockchip_hdmiv2_scdc_update_read(struct hdmi_dev *hdmi_dev)
                     m_I2CM_READ_UPDATE, v_I2CM_READ_UPDATE(1));
 }
 
-
 static int rockchip_hdmiv2_scdc_get_scambling_status(struct hdmi_dev *hdmi_dev)
 {
        int val;
@@ -260,12 +355,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 +398,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 +425,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 +439,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 +485,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 +524,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 +541,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;
+       }
 
-       for (i = 0; i < 0x28; i++)
-               pr_info("phy reg %02x val %04x\n",
-                       i, rockchip_hdmiv2_read_phy(hdmi_dev, i));
+       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;
+               }
+       }
+
+       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=%ld,TMDSCLK_ON=%ld\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 +712,29 @@ 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);
+       } else if (hdmi_dev->soctype == HDMI_SOC_RK3366) {
+               if (hdmi_dev->pixelclk > 148500000)
+                       clk_set_rate(hdmi_dev->pclk_phy, 148500000);
+               else
+                       clk_set_rate(hdmi_dev->pclk_phy, hdmi_dev->pixelclk);
+       } else if (hdmi_dev->soctype == HDMI_SOC_RK3399) {
+               clk_set_rate(hdmi_dev->pclk_phy, hdmi_dev->pixelclk);
+       }
+
        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);
@@ -440,10 +744,17 @@ static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev)
        hdmi_writel(hdmi_dev, PHY_I2CM_SLAVE, PHY_GEN2_ADDR);
 
        /* config the required PHY I2C register */
-       phy_mpll = get_phy_mpll_tab(hdmi_dev->pixelclk,
-                                   hdmi_dev->tmdsclk,
-                                   hdmi_dev->pixelrepeat - 1,
-                                   hdmi_dev->colordepth);
+       if (hdmi_dev->soctype == HDMI_SOC_RK3366 &&
+           hdmi_dev->pixelclk > 148500000)
+               phy_mpll = get_phy_mpll_tab(148500000,
+                                           hdmi_dev->tmdsclk,
+                                           hdmi_dev->pixelrepeat - 1,
+                                           hdmi_dev->colordepth);
+       else
+               phy_mpll = get_phy_mpll_tab(hdmi_dev->pixelclk,
+                                           hdmi_dev->tmdsclk,
+                                           hdmi_dev->pixelrepeat - 1,
+                                           hdmi_dev->colordepth);
        if (phy_mpll) {
                rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_OPMODE_PLLCFG,
                                          v_PREP_DIV(phy_mpll->prep_div) |
@@ -465,56 +776,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);
@@ -526,11 +839,11 @@ static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev)
        if ((stat & m_PHY_LOCK) == 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",
+                       "PHY PLL not locked: PCLK_ON=%ld,TMDSCLK_ON=%ld\n",
                        (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;
 }
 
@@ -554,58 +867,78 @@ static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv,
                     v_HSYNC_POL(hsync_pol));
 
        timing = (struct hdmi_video_timing *)hdmi_vic2timing(vpara->vic);
-       if (timing == NULL) {
+       if (!timing) {
                dev_err(hdmi_drv->dev,
                        "[%s] not found vic %d\n", __func__, vpara->vic);
                return -ENOENT;
        }
-       mode = &(timing->mode);
+       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 +957,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 +968,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 +982,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,33 +1026,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);
-
-       /*Set PreambleFilter*/
-       for (i = 0; i < 3; i++) {
-               value = (i + 1) * 11;
-               if (i == 0)             /*channel 0*/
-                       hdmi_writel(hdmi_dev, FC_CH0PREAM, value);
-               else if (i == 1)        /*channel 1*/
-                       hdmi_writel(hdmi_dev, FC_CH1PREAM, value & 0x3f);
-               else if (i == 2)        /*channel 2*/
-                       hdmi_writel(hdmi_dev, FC_CH2PREAM, value & 0x3f);
-       }
-#endif
+       hdmi_writel(hdmi_dev, FC_EXCTRLSPAC,
+                   (hdmi_dev->tmdsclk / 1000) * 50 / (256 * 512));
 
-       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 +1048,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 +1092,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 +1199,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 +1224,15 @@ 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_BYPASS */
+               0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+               0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+               0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+       },
        {       /* 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*/
@@ -936,7 +1279,7 @@ static const char coeff_csc[][24] = {
 static int rockchip_hdmiv2_video_csc(struct hdmi_dev *hdmi_dev,
                                     struct hdmi_video *vpara)
 {
-       int i, mode, interpolation, decimation, csc_scale;
+       int i, mode, interpolation, decimation, csc_scale = 0;
        const char *coeff = NULL;
        unsigned char color_depth = 0;
 
@@ -962,6 +1305,9 @@ static int rockchip_hdmiv2_video_csc(struct hdmi_dev *hdmi_dev,
                             m_CSC_DECIMODE, v_CSC_DECIMODE(decimation));
        }
 
+       mode = CSC_BYPASS;
+       csc_scale = 0;
+
        switch (vpara->vic) {
        case HDMI_720X480I_60HZ_4_3:
        case HDMI_720X576I_50HZ_4_3:
@@ -1036,18 +1382,17 @@ static int rockchip_hdmiv2_video_csc(struct hdmi_dev *hdmi_dev,
        return 0;
 }
 
-
 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)
@@ -1061,7 +1406,7 @@ static int hdmi_dev_read_edid(struct hdmi *hdmi, int block, unsigned char *buff)
 
        rockchip_hdmiv2_i2cm_reset(hdmi_dev);
 
-       /* Set DDC I2C CLK which devided from DDC_CLK to 100KHz. */
+       /* Set DDC I2C CLK which divided from DDC_CLK to 100KHz. */
        rockchip_hdmiv2_i2cm_clk_init(hdmi_dev);
 
        /* Enable I2C interrupt for reading edid */
@@ -1125,25 +1470,18 @@ 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;
 }
 
 static void hdmi_dev_config_avi(struct hdmi_dev *hdmi_dev,
                                struct hdmi_video *vpara)
 {
-       unsigned char colorimetry, ext_colorimetry, aspect_ratio, y1y0;
+       unsigned char colorimetry, ext_colorimetry = 0, 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 +1503,31 @@ 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 +1542,17 @@ 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) ||
+           (vpara->vic & HDMI_VIDEO_DISCRETE_VR))
                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 +1589,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;
 }
 
@@ -1267,11 +1642,19 @@ static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara)
                vpara->color_input = HDMI_COLOR_RGB_0_255;
 
        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));
-
+               /* before configure video, we power off phy */
+               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,15 +1665,15 @@ 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));
+               hdmi_writel(hdmi_dev, MC_CLKDIS, m_HDCPCLK_DISABLE);
        }
 
-       hdmi_writel(hdmi_dev, MC_CLKDIS, m_HDCPCLK_DISABLE);
        if (rockchip_hdmiv2_video_framecomposer(hdmi, vpara) < 0)
                return -1;
 
@@ -1304,6 +1687,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,
@@ -1320,35 +1706,40 @@ static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara)
                                            vpara->vic,
                                            HDMI_VIDEO_FORMAT_NORMAL);
                }
-               dev_info(hdmi->dev, "[%s] sucess output HDMI.\n", __func__);
+               dev_info(hdmi->dev, "[%s] success output HDMI.\n", __func__);
        } else {
-               dev_info(hdmi->dev, "[%s] sucess output DVI.\n", __func__);
+               dev_info(hdmi->dev, "[%s] success 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));
+                    m_FC_CHN_CNT | m_FC_CODING_TYPE,
+                    v_FC_CHN_CNT(audio->channel - 1) | v_FC_CODING_TYPE(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);
 }
 
@@ -1358,6 +1749,7 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
        int word_length = 0, channel = 0, mclk_fs;
        unsigned int N = 0, CTS = 0;
        int rate = 0;
+       char design_id;
 
        HDMIDBG("%s\n", __func__);
 
@@ -1372,7 +1764,7 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
 
        switch (audio->rate) {
        case HDMI_AUDIO_FS_32000:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_32K;
                if (hdmi_dev->tmdsclk >= 594000000)
                        N = N_32K_HIGHCLK;
@@ -1381,10 +1773,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_32K_LOWCLK;
                /*div a num to avoid the value is exceed 2^32(int)*/
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/1000, 32);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 32);
                break;
        case HDMI_AUDIO_FS_44100:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_441K;
                if (hdmi_dev->tmdsclk >= 594000000)
                        N = N_441K_HIGHCLK;
@@ -1393,10 +1785,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_441K_LOWCLK;
 
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/100, 441);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 100, 441);
                break;
        case HDMI_AUDIO_FS_48000:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_48K;
                if (hdmi_dev->tmdsclk >= 594000000)     /*FS_153.6*/
                        N = N_48K_HIGHCLK;
@@ -1405,10 +1797,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_48K_LOWCLK;
 
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/1000, 48);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 48);
                break;
        case HDMI_AUDIO_FS_88200:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_882K;
                if (hdmi_dev->tmdsclk >= 594000000)
                        N = N_882K_HIGHCLK;
@@ -1417,10 +1809,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_882K_LOWCLK;
 
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/100, 882);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 100, 882);
                break;
        case HDMI_AUDIO_FS_96000:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_96K;
                if (hdmi_dev->tmdsclk >= 594000000)     /*FS_153.6*/
                        N = N_96K_HIGHCLK;
@@ -1429,10 +1821,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_96K_LOWCLK;
 
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/1000, 96);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 96);
                break;
        case HDMI_AUDIO_FS_176400:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_1764K;
                if (hdmi_dev->tmdsclk >= 594000000)
                        N = N_1764K_HIGHCLK;
@@ -1441,10 +1833,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_1764K_LOWCLK;
 
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/100, 1764);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 100, 1764);
                break;
        case HDMI_AUDIO_FS_192000:
-               mclk_fs = FS_64;
+               mclk_fs = FS_128;
                rate = AUDIO_192K;
                if (hdmi_dev->tmdsclk >= 594000000)     /*FS_153.6*/
                        N = N_192K_HIGHCLK;
@@ -1453,7 +1845,7 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                else
                        N = N_192K_LOWCLK;
 
-               CTS = CALC_CTS(N, hdmi_dev->tmdsclk/1000, 192);
+               CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 192);
                break;
        default:
                dev_err(hdmi_dev->hdmi->dev,
@@ -1484,6 +1876,7 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                     v_AUD_PACK_LAYOUT((audio->channel > 2) ? 1 : 0));
 
        if (hdmi_dev->audiosrc == HDMI_AUDIO_SRC_SPDIF) {
+               mclk_fs = FS_128;
                hdmi_msk_reg(hdmi_dev, AUD_CONF0,
                             m_I2S_SEL, v_I2S_SEL(AUDIO_SPDIF_GPA));
                hdmi_msk_reg(hdmi_dev, AUD_SPDIF1,
@@ -1504,7 +1897,47 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
                hdmi_msk_reg(hdmi_dev, AUD_CONF0,
                             m_SW_AUD_FIFO_RST, v_SW_AUD_FIFO_RST(1));
                hdmi_writel(hdmi_dev, MC_SWRSTZREQ, 0xF7);
+               design_id = hdmi_readl(hdmi_dev, DESIGN_ID);
+               if (design_id >= 0x21)
+                       hdmi_writel(hdmi_dev, AUD_CONF2, 0x4);
+               else
+                       hdmi_writel(hdmi_dev, AUD_CONF2, 0x0);
                usleep_range(90, 100);
+               /*
+                * when we try to use hdmi nlpcm mode
+                * we should use set AUD_CONF2 to open this route and set
+                * word_length to 24bit for b.p.c.u.v with 16bit raw data
+                * when the bitstream data  up to 8 channel, we should use
+                * the hdmi hbr mode
+                * HBR Mode : Dolby TrueHD
+                *            Dolby Atmos
+                *            DTS-HDMA
+                * NLPCM Mode :
+                * FS_32000 FS_44100 FS_48000 : Dolby Digital &  DTS
+                * FS_176400 FS_192000        : Dolby Digital Plus
+                */
+               if (audio->type == HDMI_AUDIO_NLPCM) {
+                       if (channel == I2S_CHANNEL_7_8) {
+                               HDMIDBG("hbr mode.\n");
+                               hdmi_writel(hdmi_dev, AUD_CONF2, 0x1);
+                               word_length = I2S_24BIT_SAMPLE;
+                       } else if ((audio->rate == HDMI_AUDIO_FS_32000) ||
+                                  (audio->rate == HDMI_AUDIO_FS_44100) ||
+                                  (audio->rate == HDMI_AUDIO_FS_48000) ||
+                                  (audio->rate == HDMI_AUDIO_FS_176400) ||
+                                  (audio->rate == HDMI_AUDIO_FS_192000)) {
+                               HDMIDBG("nlpcm mode.\n");
+                               hdmi_writel(hdmi_dev, AUD_CONF2, 0x2);
+                               word_length = I2S_24BIT_SAMPLE;
+                       } else {
+                               hdmi_writel(hdmi_dev, AUD_CONF2, 0x0);
+                       }
+               } else {
+                       if (design_id >= 0x21)
+                               hdmi_writel(hdmi_dev, AUD_CONF2, 0x4);
+                       else
+                               hdmi_writel(hdmi_dev, AUD_CONF2, 0x0);
+               }
                hdmi_msk_reg(hdmi_dev, AUD_CONF0,
                             m_I2S_SEL | m_I2S_IN_EN,
                             v_I2S_SEL(AUDIO_I2S) | v_I2S_IN_EN(channel));
@@ -1534,8 +1967,10 @@ static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio)
        /* set channel status register */
        hdmi_msk_reg(hdmi_dev, FC_AUDSCHNLS7,
                     m_AUDIO_SAMPLE_RATE, v_AUDIO_SAMPLE_RATE(rate));
-       /* hdmi_writel(hdmi_dev, FC_AUDSCHNLS2, 0x1); */
-       /* hdmi_writel(hdmi_dev, FC_AUDSCHNLS8, ((~rate) << 4) | 0x2); */
+       hdmi_writel(hdmi_dev, FC_AUDSCHNLS8, ((~rate) << 4) | 0x2);
+
+       hdmi_msk_reg(hdmi_dev, AUD_CONF0,
+                    m_SW_AUD_FIFO_RST, v_SW_AUD_FIFO_RST(1));
 
        hdmi_dev_config_aai(hdmi_dev, audio);
 
@@ -1545,37 +1980,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;
 }
@@ -1585,8 +2028,9 @@ static int hdmi_dev_insert(struct hdmi *hdmi)
        struct hdmi_dev *hdmi_dev = hdmi->property->priv;
 
        HDMIDBG("%s\n", __func__);
-       hdmi_writel(hdmi_dev, MC_CLKDIS, m_HDCPCLK_DISABLE);
-       return HDMI_ERROR_SUCESS;
+       if (!hdmi->uboot)
+               hdmi_writel(hdmi_dev, MC_CLKDIS, m_HDCPCLK_DISABLE);
+       return HDMI_ERROR_SUCCESS;
 }
 
 static int hdmi_dev_remove(struct hdmi *hdmi)
@@ -1594,9 +2038,11 @@ 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;
+       return HDMI_ERROR_SUCCESS;
 }
 
 static int hdmi_dev_enable(struct hdmi *hdmi)
@@ -1608,7 +2054,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;
 }
 
@@ -1644,37 +2090,56 @@ void rockchip_hdmiv2_dev_initial(struct hdmi_dev *hdmi_dev)
 {
        struct hdmi *hdmi = hdmi_dev->hdmi;
 
+       /*lcdc source select*/
+       if (hdmi_dev->soctype == HDMI_SOC_RK3288) {
+               grf_writel(HDMI_SEL_LCDC(hdmi->property->videosrc, 4),
+                          RK3288_GRF_SOC_CON6);
+               /* select GPIO7_C0 as cec pin */
+               grf_writel(((1 << 12) | (1 << 28)), RK3288_GRF_SOC_CON8);
+       } else if (hdmi_dev->soctype == HDMI_SOC_RK3399) {
+               regmap_write(hdmi_dev->grf_base,
+                            RK3399_GRF_SOC_CON20,
+                            HDMI_SEL_LCDC(hdmi->property->videosrc, 6));
+       }
+
        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*/
+       /*mute unnecessary interrupt, 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);
        hdmi_writel(hdmi_dev, IH_MUTE_I2CMPHY_STAT0, 0xff);
        hdmi_writel(hdmi_dev, IH_MUTE_AHBDMAAUD_STAT0, 0xff);
 
-       /* disable hdcp interrup */
+       /* disable hdcp interrupt */
        hdmi_writel(hdmi_dev, A_APIINTMSK, 0xff);
        hdmi_writel(hdmi_dev, PHY_MASK, 0xf1);
 
@@ -1709,11 +2174,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 */
@@ -1726,18 +2196,25 @@ 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 */
        if (hdcp2_int) {
                hdmi_writel(hdmi_dev, HDCP2REG_STAT, hdcp2_int);
                pr_info("hdcp2_int is 0x%02x\n", hdcp2_int);
+               if ((hdcp2_int & m_HDCP2_AUTH_FAIL ||
+                    hdcp2_int & m_HDCP2_AUTH_LOST) &&
+                   hdmi_dev->hdcp2_start) {
+                       pr_info("hdcp2 failed or lost\n");
+                       hdmi_dev->hdcp2_start();
+               }
        }
        return IRQ_HANDLED;
 }