video: rockchip: rk322x: use htotal to adjust fps
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / lcdc / rk322x_lcdc.c
index 10b926bce0115bea312bec0591d21e18515b2c8c..7a9b67d7a1f766b8894184585779e4b59d9fd76d 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
 #include <linux/rockchip-iovmm.h>
 #include <asm/div64.h>
 #include <linux/uaccess.h>
@@ -34,6 +36,8 @@
 #include <linux/rockchip/grf.h>
 #include <linux/rockchip/common.h>
 #include <dt-bindings/clock/rk_system_status.h>
+#include <soc/rockchip/rkfb_dmc.h>
+#include <linux/of_gpio.h>
 
 #include "rk322x_lcdc.h"
 
@@ -49,17 +53,65 @@ module_param(dbg_thresd, int, S_IRUGO | S_IWUSR);
        } while (0)
 
 static struct rk_lcdc_win rk322x_vop_win[] = {
-       { .name = "win0", .id = VOP_WIN0},
-       { .name = "win1", .id = VOP_WIN1},
-       { .name = "hwc",  .id = VOP_HWC}
+       { .name = "win0",
+         .id = VOP_WIN0,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_SCALE | SUPPORT_YUV |
+                               SUPPORT_YUV10BIT,
+         .property.max_input_x = 4096,
+         .property.max_input_y = 2304},
+       { .name = "win1",
+         .id = VOP_WIN1,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_SCALE | SUPPORT_YUV |
+                               SUPPORT_YUV10BIT,
+         .property.max_input_x = 4096,
+         .property.max_input_y = 2304},
+       {
+         .name = "hwc",
+         .id = VOP_HWC,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_HWC_LAYER,
+         .property.max_input_x = 128,
+         .property.max_input_y = 128
+       }
 };
 
 static struct rk_lcdc_win rk3399_vop_win[] = {
-       { .name = "win0", .id = VOP_WIN0},
-       { .name = "win1", .id = VOP_WIN1},
-       { .name = "win2", .id = VOP_WIN2},
-       { .name = "win3", .id = VOP_WIN3},
-       { .name = "hwc",  .id = VOP_HWC}
+       { .name = "win0",
+         .id = VOP_WIN0,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_SCALE | SUPPORT_YUV |
+                               SUPPORT_YUV10BIT,
+         .property.max_input_x = 4096,
+         .property.max_input_y = 2304},
+       { .name = "win1",
+         .id = VOP_WIN1,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_SCALE | SUPPORT_YUV |
+                               SUPPORT_YUV10BIT,
+         .property.max_input_x = 4096,
+         .property.max_input_y = 2304},
+       { .name = "win2",
+         .id = VOP_WIN2,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_MULTI_AREA,
+         .property.max_input_x = 4096,
+         .property.max_input_y = 2304},
+       { .name = "win3",
+         .id = VOP_WIN3,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_MULTI_AREA,
+         .property.max_input_x = 4096,
+         .property.max_input_y = 2304},
+       {
+         .name = "hwc",
+         .id = VOP_HWC,
+         .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST |
+                               SUPPORT_HWC_LAYER,
+         .property.max_input_x = 128,
+         .property.max_input_y = 128
+       }
 };
 
 static const struct vop_data rk322x_data = {
@@ -246,15 +298,20 @@ static int vop_set_lut(struct rk_lcdc_driver *dev_drv, int *dsp_lut)
                int __iomem *c;
 
                v = dsp_lut[i];
-               c = vop_dev->dsp_lut_addr_base + (i << 2);
-               b = (v & 0xff) << 2;
-               g = (v & 0xff00) << 4;
-               r = (v & 0xff0000) << 6;
-               v = r + g + b;
-               for (j = 0; j < 4; j++) {
+               if (dev_drv->id == 0) {
+                       c = vop_dev->dsp_lut_addr_base + (i << 2);
+                       b = (v & 0xff) << 2;
+                       g = (v & 0xff00) << 4;
+                       r = (v & 0xff0000) << 6;
+                       v = r + g + b;
+                       for (j = 0; j < 4; j++) {
+                               writel_relaxed(v, c);
+                               v += (1 + (1 << 10) + (1 << 20));
+                               c++;
+                       }
+               } else {
+                       c = vop_dev->dsp_lut_addr_base + i;
                        writel_relaxed(v, c);
-                       v += (1 + (1 << 10) + (1 << 20));
-                       c++;
                }
        }
        vop_msk_reg(vop_dev, DSP_CTRL1, V_DSP_LUT_EN(1));
@@ -316,6 +373,9 @@ static int vop_clk_enable(struct vop_device *vop_dev)
                        clk_prepare_enable(vop_dev->hclk_noc);
                if (vop_dev->aclk_noc)
                        clk_prepare_enable(vop_dev->aclk_noc);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
+               pm_runtime_get_sync(vop_dev->dev);
+#endif
                spin_lock(&vop_dev->reg_lock);
                vop_dev->clk_on = 1;
                spin_unlock(&vop_dev->reg_lock);
@@ -331,6 +391,9 @@ static int vop_clk_disable(struct vop_device *vop_dev)
                vop_dev->clk_on = 0;
                spin_unlock(&vop_dev->reg_lock);
                mdelay(25);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
+               pm_runtime_put_sync(vop_dev->dev);
+#endif
                clk_disable_unprepare(vop_dev->dclk);
                clk_disable_unprepare(vop_dev->hclk);
                clk_disable_unprepare(vop_dev->aclk);
@@ -419,6 +482,8 @@ static int vop_win_direct_en(struct rk_lcdc_driver *drv,
 {
        struct vop_device *vop_dev =
            container_of(drv, struct vop_device, driver);
+
+       drv->win[win_id]->state = en;
        if (win_id == 0)
                win0_enable(vop_dev, en);
        else if (win_id == 1)
@@ -577,9 +642,13 @@ static int vop_pre_init(struct rk_lcdc_driver *dev_drv)
        vop_writel(vop_dev, FRC_LOWER11_0, 0xdeb77deb);
        vop_writel(vop_dev, FRC_LOWER11_1, 0xed7bb7de);
 
-       vop_msk_reg(vop_dev, SYS_CTRL, V_AUTO_GATING_EN(0));
+       if (!dev_drv->cabc_mode)
+               vop_msk_reg(vop_dev, SYS_CTRL, V_AUTO_GATING_EN(0));
        vop_msk_reg(vop_dev, DSP_CTRL1, V_DITHER_UP_EN(1));
        vop_cfg_done(vop_dev);
+       if ((dev_drv->cur_screen->refresh_mode == SCREEN_CMD_MODE) &&
+           (support_uboot_display() == 0))
+               vop_msk_reg(vop_dev, SYS_CTRL, V_EDPI_WMS_MODE(1));
        vop_dev->pre_init = true;
 
        return 0;
@@ -630,7 +699,7 @@ static int rk3399_vop_win_csc_cfg(struct rk_lcdc_driver *dev_drv)
        int output_color = dev_drv->output_color;
        int i;
 
-       for (i = 0; i < dev_drv->lcdc_win_num && i <= 4; i++) {
+       for (i = 0; i < dev_drv->lcdc_win_num && i < 4; i++) {
                struct rk_lcdc_win *win = dev_drv->win[i];
                int shift = i * 8;
                u64 val = V_WIN0_YUV2YUV_EN(0) | V_WIN0_YUV2YUV_R2Y_EN(0) |
@@ -656,36 +725,44 @@ static int rk3399_vop_win_csc_cfg(struct rk_lcdc_driver *dev_drv)
                        } else if (win->colorspace == CSC_BT2020) {
                                val |= V_WIN0_YUV2YUV_EN(1);
                                LOAD_CSC(vop_dev, Y2R, csc_y2r_bt2020, i);
-                               LOAD_CSC(vop_dev, 3x3, csc_r2r_bt2020to709, i);
+                               LOAD_CSC(vop_dev, R2R, csc_r2r_bt2020to709, i);
                        }
                } else if (output_color == COLOR_YCBCR ||
                                output_color == COLOR_YCBCR_BT709) {
                        if (!(IS_YUV(win->area[0].fmt_cfg) ||
                              win->area[0].yuyv_fmt)) {
                                val |= V_WIN0_YUV2YUV_R2Y_EN(1);
-                               LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full, i);
+                               if ((win->id == 0) || (win->id == 1))
+                                       LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full_10, i);
+                               else
+                                       val |= V_WIN0_YUV2YUV_R2Y_MODE(VOP_R2Y_CSC_BT709);
+
                        } else if (win->colorspace == CSC_BT2020) {
                                val |= V_WIN0_YUV2YUV_EN(1) |
                                        V_WIN0_YUV2YUV_Y2R_EN(1) |
                                        V_WIN0_YUV2YUV_R2Y_EN(1);
-                               LOAD_CSC(vop_dev, R2Y, csc_y2r_bt2020, i);
-                               LOAD_CSC(vop_dev, R2Y, csc_r2r_bt2020to709, i);
-                               LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full, i);
+                               LOAD_CSC(vop_dev, Y2R, csc_y2r_bt2020, i);
+                               LOAD_CSC(vop_dev, R2R, csc_r2r_bt2020to709, i);
+                               LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full_10, i);
                        }
                } else if (output_color == COLOR_YCBCR_BT2020) {
                        if (!(IS_YUV(win->area[0].fmt_cfg) ||
                              win->area[0].yuyv_fmt)) {
                                val |= V_WIN0_YUV2YUV_R2Y_EN(1) |
                                        V_WIN0_YUV2YUV_EN(1);
-                               LOAD_CSC(vop_dev, R2Y, csc_r2r_bt709to2020, i);
-                               LOAD_CSC(vop_dev, R2Y, csc_r2y_bt2020, i);
+                               if ((win->id == 0) || (win->id == 1)) {
+                                       LOAD_CSC(vop_dev, R2R, csc_r2r_bt709to2020, i);
+                                       LOAD_CSC(vop_dev, R2Y, csc_r2y_bt2020, i);
+                               } else {
+                                       val |= V_WIN0_YUV2YUV_R2Y_MODE(VOP_R2Y_CSC_BT2020);
+                               }
                        } else if (win->colorspace == CSC_BT601 ||
                                        win->colorspace == CSC_BT709) {
                                val |= V_WIN0_YUV2YUV_Y2R_EN(1) |
                                        V_WIN0_YUV2YUV_R2Y_EN(1) |
                                        V_WIN0_YUV2YUV_EN(1);
-                               LOAD_CSC(vop_dev, R2Y, csc_y2r_bt709_full, i);
-                               LOAD_CSC(vop_dev, R2Y, csc_r2r_bt709to2020, i);
+                               LOAD_CSC(vop_dev, Y2R, csc_y2r_bt709_full_10, i);
+                               LOAD_CSC(vop_dev, R2R, csc_r2r_bt709to2020, i);
                                LOAD_CSC(vop_dev, R2Y, csc_r2y_bt2020, i);
                        }
                }
@@ -802,7 +879,7 @@ static int vop_post_csc_cfg(struct rk_lcdc_driver *dev_drv)
        struct vop_device *vop_dev =
            container_of(dev_drv, struct vop_device, driver);
        int output_color = dev_drv->output_color;
-       int win_csc, overlay_mode;
+       int win_csc = 0, overlay_mode = 0;
        u64 val;
 
        if (VOP_CHIP(vop_dev) == VOP_RK322X) {
@@ -1041,9 +1118,10 @@ static int vop_alpha_cfg(struct rk_lcdc_driver *dev_drv, int win_id)
        struct alpha_config alpha_config;
        u64 val;
        int ppixel_alpha = 0, global_alpha = 0, i;
-       u32 src_alpha_ctl, dst_alpha_ctl;
+       u32 src_alpha_ctl = 0, dst_alpha_ctl = 0;
        int alpha_en = 1;
 
+       memset(&alpha_config, 0, sizeof(struct alpha_config));
        for (i = 0; i < win->area_num; i++) {
                ppixel_alpha |= ((win->area[i].format == ARGB888) ||
                                 (win->area[i].format == FBDC_ARGB_888) ||
@@ -1252,7 +1330,7 @@ static int vop_axi_gather_cfg(struct vop_device *vop_dev,
 static int vop_fbdc_reg_update(struct vop_device *vop_dev, int win_id)
 {
        struct rk_lcdc_win *win = vop_dev->driver.win[win_id];
-       u32 val;
+       u64 val;
 
        val = V_VOP_FBDC_WIN_SEL(win_id) |
                V_AFBCD_HREG_PIXEL_PACKING_FMT(win->area[0].fbdc_fmt_cfg) |
@@ -1282,10 +1360,10 @@ static int vop_init_fbdc_config(struct vop_device *vop_dev, int win_id)
                return 0;
        }
 
-       win->area[0].fbdc_mb_width = win->area[0].xact;
+       win->area[0].fbdc_mb_width = win->area[0].xvir;
        win->area[0].fbdc_mb_height = win->area[0].yact;
        win->area[0].fbdc_cor_en = 0; /* hreg_block_split */
-       win->area[0].fbdc_fmt_cfg |= 0 << 4;
+       win->area[0].fbdc_fmt_cfg |= AFBDC_YUV_COLOR_TRANSFORM << 4;
 
        return 0;
 }
@@ -1298,10 +1376,17 @@ static int vop_win_0_1_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
        u64 val;
        u32 off;
        int format;
+       struct rk_win_property *win_property =
+                               &dev_drv->win[win_id]->property;
 
        off = win_id * 0x40;
 
        if (win->state == 1) {
+               if (!(win_property->feature & SUPPORT_HW_EXIST)) {
+                       pr_err("vop[%d] win[%d] hardware unsupport\n",
+                              vop_dev->id, win_id);
+                       return 0;
+               }
                vop_axi_gather_cfg(vop_dev, win);
                if (win->area[0].fbdc_en)
                        vop_fbdc_reg_update(vop_dev, win_id);
@@ -1391,13 +1476,6 @@ static int vop_win_0_1_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
                val = V_WIN0_HS_FACTOR_CBR(win->scale_cbcr_x) |
                    V_WIN0_VS_FACTOR_CBR(win->scale_cbcr_y);
                vop_writel(vop_dev, WIN0_SCL_FACTOR_CBR + off, val);
-               if (win->alpha_en == 1) {
-                       vop_alpha_cfg(dev_drv, win_id);
-               } else {
-                       val = V_WIN0_SRC_ALPHA_EN(0);
-                       vop_msk_reg(vop_dev, WIN0_SRC_ALPHA_CTRL + off, val);
-               }
-
        } else {
                val = V_WIN0_EN(win->state);
                vop_msk_reg(vop_dev, WIN0_CTRL0 + off, val);
@@ -1434,11 +1512,18 @@ static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
        struct rk_lcdc_win *win = dev_drv->win[win_id];
        unsigned int off;
        u64 val;
+       struct rk_win_property *win_property =
+                               &dev_drv->win[win_id]->property;
 
        off = (win_id - 2) * 0x50;
        area_xst(win, win->area_num);
 
        if (win->state == 1) {
+               if (!(win_property->feature & SUPPORT_HW_EXIST)) {
+                       pr_err("vop[%d] win[%d] hardware unsupport\n",
+                              vop_dev->id, win_id);
+                       return 0;
+               }
                vop_axi_gather_cfg(vop_dev, win);
                if (win->area[0].fbdc_en)
                        vop_fbdc_reg_update(vop_dev, win_id);
@@ -1454,8 +1539,8 @@ static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
                        val = V_WIN2_VIR_STRIDE0(win->area[0].y_vir_stride);
                        vop_msk_reg(vop_dev, WIN2_VIR0_1 + off, val);
 
-                       val = V_WIN2_DSP_WIDTH0(win->area[0].xsize) |
-                           V_WIN2_DSP_HEIGHT0(win->area[0].ysize);
+                       val = V_WIN2_DSP_WIDTH0(win->area[0].xsize - 1) |
+                           V_WIN2_DSP_HEIGHT0(win->area[0].ysize - 1);
                        vop_writel(vop_dev, WIN2_DSP_INFO0 + off, val);
                        val = V_WIN2_DSP_XST0(win->area[0].dsp_stx) |
                            V_WIN2_DSP_YST0(win->area[0].dsp_sty);
@@ -1474,8 +1559,8 @@ static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
                        val = V_WIN2_VIR_STRIDE1(win->area[1].y_vir_stride);
                        vop_msk_reg(vop_dev, WIN2_VIR0_1 + off, val);
 
-                       val = V_WIN2_DSP_WIDTH1(win->area[1].xsize) |
-                           V_WIN2_DSP_HEIGHT1(win->area[1].ysize);
+                       val = V_WIN2_DSP_WIDTH1(win->area[1].xsize - 1) |
+                           V_WIN2_DSP_HEIGHT1(win->area[1].ysize - 1);
                        vop_writel(vop_dev, WIN2_DSP_INFO1 + off, val);
                        val = V_WIN2_DSP_XST1(win->area[1].dsp_stx) |
                            V_WIN2_DSP_YST1(win->area[1].dsp_sty);
@@ -1494,8 +1579,8 @@ static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
                        val = V_WIN2_VIR_STRIDE2(win->area[2].y_vir_stride);
                        vop_msk_reg(vop_dev, WIN2_VIR2_3 + off, val);
 
-                       val = V_WIN2_DSP_WIDTH2(win->area[2].xsize) |
-                           V_WIN2_DSP_HEIGHT2(win->area[2].ysize);
+                       val = V_WIN2_DSP_WIDTH2(win->area[2].xsize - 1) |
+                           V_WIN2_DSP_HEIGHT2(win->area[2].ysize - 1);
                        vop_writel(vop_dev, WIN2_DSP_INFO2 + off, val);
                        val = V_WIN2_DSP_XST2(win->area[2].dsp_stx) |
                            V_WIN2_DSP_YST2(win->area[2].dsp_sty);
@@ -1514,8 +1599,8 @@ static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
                        val = V_WIN2_VIR_STRIDE3(win->area[3].y_vir_stride);
                        vop_msk_reg(vop_dev, WIN2_VIR2_3 + off, val);
 
-                       val = V_WIN2_DSP_WIDTH3(win->area[3].xsize) |
-                           V_WIN2_DSP_HEIGHT3(win->area[3].ysize);
+                       val = V_WIN2_DSP_WIDTH3(win->area[3].xsize - 1) |
+                           V_WIN2_DSP_HEIGHT3(win->area[3].ysize - 1);
                        vop_writel(vop_dev, WIN2_DSP_INFO3 + off, val);
                        val = V_WIN2_DSP_XST3(win->area[3].dsp_stx) |
                            V_WIN2_DSP_YST3(win->area[3].dsp_sty);
@@ -1524,13 +1609,6 @@ static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
                        val = V_WIN2_MST3_EN(0);
                        vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
                }
-
-               if (win->alpha_en == 1) {
-                       vop_alpha_cfg(dev_drv, win_id);
-               } else {
-                       val = V_WIN2_SRC_ALPHA_EN(0);
-                       vop_msk_reg(vop_dev, WIN2_SRC_ALPHA_CTRL + off, val);
-               }
        } else {
                val = V_WIN2_EN(win->state) | V_WIN2_MST0_EN(0) |
                    V_WIN2_MST1_EN(0) | V_WIN2_MST2_EN(0) | V_WIN2_MST3_EN(0);
@@ -1548,40 +1626,33 @@ static int vop_hwc_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
        unsigned int hwc_size = 0;
        u64 val;
 
+       if ((win->area[0].xsize == 32) && (win->area[0].ysize == 32)) {
+               hwc_size = 0;
+       } else if ((win->area[0].xsize == 64) && (win->area[0].ysize == 64)) {
+               hwc_size = 1;
+       } else if ((win->area[0].xsize == 96) && (win->area[0].ysize == 96)) {
+               hwc_size = 2;
+       } else if ((win->area[0].xsize == 128) &&
+                  (win->area[0].ysize == 128)) {
+               hwc_size = 3;
+       } else {
+               dev_err(vop_dev->dev, "un supported hwc size[%dx%d]!\n",
+                               win->area[0].xsize, win->area[0].ysize);
+               return -EINVAL;
+       }
+
        if (win->state == 1) {
                vop_axi_gather_cfg(vop_dev, win);
                val = V_HWC_EN(1) | V_HWC_DATA_FMT(win->area[0].fmt_cfg) |
                    V_HWC_RB_SWAP(win->area[0].swap_rb);
                vop_msk_reg(vop_dev, HWC_CTRL0, val);
 
-               if ((win->area[0].xsize == 32) && (win->area[0].ysize == 32))
-                       hwc_size = 0;
-               else if ((win->area[0].xsize == 64) &&
-                        (win->area[0].ysize == 64))
-                       hwc_size = 1;
-               else if ((win->area[0].xsize == 96) &&
-                        (win->area[0].ysize == 96))
-                       hwc_size = 2;
-               else if ((win->area[0].xsize == 128) &&
-                        (win->area[0].ysize == 128))
-                       hwc_size = 3;
-               else
-                       dev_err(vop_dev->dev, "un supported hwc size[%dx%d]!\n",
-                               win->area[0].xsize, win->area[0].ysize);
-
                val = V_HWC_SIZE(hwc_size);
                vop_msk_reg(vop_dev, HWC_CTRL0, val);
 
                val = V_HWC_DSP_XST(win->area[0].dsp_stx) |
                    V_HWC_DSP_YST(win->area[0].dsp_sty);
                vop_msk_reg(vop_dev, HWC_DSP_ST, val);
-
-               if (win->alpha_en == 1) {
-                       vop_alpha_cfg(dev_drv, win_id);
-               } else {
-                       val = V_WIN2_SRC_ALPHA_EN(0);
-                       vop_msk_reg(vop_dev, HWC_SRC_ALPHA_CTRL, val);
-               }
        } else {
                val = V_HWC_EN(win->state);
                vop_msk_reg(vop_dev, HWC_CTRL0, val);
@@ -1739,6 +1810,10 @@ static int vop_config_timing(struct rk_lcdc_driver *dev_drv)
                vop_msk_reg(vop_dev, LINE_FLAG, val);
        }
        vop_post_cfg(dev_drv);
+       if ((x_res <= VOP_INPUT_MAX_WIDTH / 2) && (vop_dev->id == 0))
+               vop_msk_reg(vop_dev, SYS_CTRL, V_POST_LB_MODE(1));
+       else
+               vop_msk_reg(vop_dev, SYS_CTRL, V_POST_LB_MODE(0));
 
        return 0;
 }
@@ -1847,7 +1922,7 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
        struct vop_device *vop_dev =
            container_of(dev_drv, struct vop_device, driver);
        struct rk_screen *screen = dev_drv->cur_screen;
-       u64 val;
+       u64 val = 0;
 
        if (unlikely(!vop_dev->clk_on)) {
                pr_info("%s,clk_on = %d\n", __func__, vop_dev->clk_on);
@@ -1890,6 +1965,18 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
                                | V_PRE_DITHER_DOWN_EN(1) |
                                V_DITHER_DOWN_SEL(0) | V_DITHER_DOWN_MODE(0);
                        break;
+               case OUT_S888x:
+                       face = OUT_S888x;
+                       val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1)
+                               | V_PRE_DITHER_DOWN_EN(1) |
+                               V_DITHER_DOWN_SEL(0) | V_DITHER_DOWN_MODE(0);
+                       break;
+               case OUT_S888:
+                       face = OUT_S888;
+                       val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1)
+                               | V_PRE_DITHER_DOWN_EN(1) |
+                               V_DITHER_DOWN_SEL(0) | V_DITHER_DOWN_MODE(0);
+                       break;
                case OUT_YUV_420:
                        face = OUT_YUV_420;
                        dclk_ddr = 1;
@@ -1906,6 +1993,20 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
                                V_DITHER_DOWN_SEL(0) |
                                V_DITHER_DOWN_MODE(0);
                        break;
+               case OUT_YUV_422:
+                       face = OUT_YUV_422;
+                       val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1) |
+                               V_PRE_DITHER_DOWN_EN(1) |
+                               V_DITHER_DOWN_SEL(0) |
+                               V_DITHER_DOWN_MODE(0);
+                       break;
+               case OUT_YUV_422_10BIT:
+                       face = OUT_YUV_422;
+                       val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1) |
+                               V_PRE_DITHER_DOWN_EN(0) |
+                               V_DITHER_DOWN_SEL(0) |
+                               V_DITHER_DOWN_MODE(0);
+                       break;
                case OUT_P101010:
                        face = OUT_P101010;
                        val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1) |
@@ -1934,31 +2035,88 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
                        vop_msk_reg(vop_dev, SYS_CTRL, val);
                        break;
                case SCREEN_HDMI:
+                       if ((VOP_CHIP(vop_dev) == VOP_RK3399) &&
+                           ((screen->face == OUT_P888) ||
+                            (screen->face == OUT_P101010))) {
+                               if (vop_dev->id == 0)
+                                       face = OUT_P101010; /*RGB 10bit output*/
+                               else
+                                       face = OUT_P888;
+                       }
                        val = V_HDMI_OUT_EN(1) | V_SW_UV_OFFSET_EN(0);
                        vop_msk_reg(vop_dev, SYS_CTRL, val);
+                       val = V_HDMI_HSYNC_POL(screen->pin_hsync) |
+                               V_HDMI_VSYNC_POL(screen->pin_vsync) |
+                               V_HDMI_DEN_POL(screen->pin_den) |
+                               V_HDMI_DCLK_POL(screen->pin_dclk);
+                       /*hsync vsync den dclk polo,dither */
+                       vop_msk_reg(vop_dev, DSP_CTRL1, val);
                        break;
                case SCREEN_RGB:
                case SCREEN_LVDS:
                        val = V_RGB_OUT_EN(1);
                        vop_msk_reg(vop_dev, SYS_CTRL, val);
+                       break;
                case SCREEN_MIPI:
                        val = V_MIPI_OUT_EN(1);
                        vop_msk_reg(vop_dev, SYS_CTRL, val);
+                       val = V_MIPI_HSYNC_POL(screen->pin_hsync) |
+                               V_MIPI_VSYNC_POL(screen->pin_vsync) |
+                               V_MIPI_DEN_POL(screen->pin_den) |
+                               V_MIPI_DCLK_POL(screen->pin_dclk);
+                       /*hsync vsync den dclk polo,dither */
+                       vop_msk_reg(vop_dev, DSP_CTRL1, val);
+                       break;
+               case SCREEN_DUAL_MIPI:
+                       val = V_MIPI_OUT_EN(1) | V_MIPI_DUAL_CHANNEL_EN(1);
+                       vop_msk_reg(vop_dev, SYS_CTRL, val);
+                       val = V_MIPI_HSYNC_POL(screen->pin_hsync) |
+                               V_MIPI_VSYNC_POL(screen->pin_vsync) |
+                               V_MIPI_DEN_POL(screen->pin_den) |
+                               V_MIPI_DCLK_POL(screen->pin_dclk);
+                       /*hsync vsync den dclk polo,dither */
+                       vop_msk_reg(vop_dev, DSP_CTRL1, val);
+                       break;
                case SCREEN_EDP:
+                       if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+                               if (vop_dev->id == 0)
+                                       face = OUT_P101010;
+                               else
+                                       face = OUT_P888;
+                       }
                        val = V_EDP_OUT_EN(1);
                        vop_msk_reg(vop_dev, SYS_CTRL, val);
+                       val = V_EDP_HSYNC_POL(screen->pin_hsync) |
+                               V_EDP_VSYNC_POL(screen->pin_vsync) |
+                               V_EDP_DEN_POL(screen->pin_den) |
+                               V_EDP_DCLK_POL(screen->pin_dclk);
+                       /*hsync vsync den dclk polo,dither */
+                       vop_msk_reg(vop_dev, DSP_CTRL1, val);
+                       break;
+               case SCREEN_DP:
+                       dclk_ddr = 0;
+                       if ((VOP_CHIP(vop_dev) == VOP_RK3399) &&
+                           ((screen->face == OUT_P888) ||
+                            (screen->face == OUT_P101010))) {
+                               if (vop_dev->id == 0)
+                                       face = OUT_P101010;
+                               else
+                                       face = OUT_P888;
+                       }
+                       val = V_DP_OUT_EN(1);
+                       vop_msk_reg(vop_dev, SYS_CTRL, val);
+                       val = V_DP_HSYNC_POL(screen->pin_hsync) |
+                               V_DP_VSYNC_POL(screen->pin_vsync) |
+                               V_DP_DEN_POL(screen->pin_den) |
+                               V_DP_DCLK_POL(screen->pin_dclk);
+                       /*hsync vsync den dclk polo,dither */
+                       vop_msk_reg(vop_dev, DSP_CTRL1, val);
                        break;
                default:
                        dev_err(vop_dev->dev, "un supported interface[%d]!\n",
                                screen->type);
                        break;
                }
-               val = V_HDMI_HSYNC_POL(screen->pin_hsync) |
-                       V_HDMI_VSYNC_POL(screen->pin_vsync) |
-                       V_HDMI_DEN_POL(screen->pin_den) |
-                       V_HDMI_DCLK_POL(screen->pin_dclk);
-               /*hsync vsync den dclk polo,dither */
-               vop_msk_reg(vop_dev, DSP_CTRL1, val);
 
                if (screen->color_mode == COLOR_RGB)
                        dev_drv->overlay_mode = VOP_RGB_DOMAIN;
@@ -2023,6 +2181,8 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
        return 0;
 }
 
+static int vop_early_suspend(struct rk_lcdc_driver *dev_drv);
+static int vop_early_resume(struct rk_lcdc_driver *dev_drv);
 /*enable layer,open:1,enable;0 disable*/
 static void vop_layer_enable(struct vop_device *vop_dev,
                             unsigned int win_id, bool open)
@@ -2047,14 +2207,50 @@ static void vop_layer_enable(struct vop_device *vop_dev,
                                              vop_dev->driver.win[win_id]);
                        vop_cfg_done(vop_dev);
                }
-               /* if no layer used,disable lcdc */
-               if (!vop_dev->atv_layer_cnt) {
+       }
+       spin_unlock(&vop_dev->reg_lock);
+       /* if no layer used,disable lcdc */
+       if (vop_dev->prop == EXTEND) {
+               if (!vop_dev->atv_layer_cnt && !open) {
+                       if (!wait_event_timeout(vop_dev->wait_dmc_queue,
+                                               !vop_dev->dmc_in_process, HZ / 5))
+                               dev_warn(vop_dev->dev,
+                                        "Timeout waiting for dmc when vop disable\n");
+
+                       vop_dev->vop_switch_status = 1;
+                       vop_early_suspend(&vop_dev->driver);
                        dev_info(vop_dev->dev,
                                 "no layer is used,go to standby!\n");
                        vop_dev->standby = 1;
+
+                       vop_dev->vop_switch_status = 0;
+                       wake_up(&vop_dev->wait_vop_switch_queue);
+                       /*
+                        * if clsoe enxtend vop need to enable dmc again.
+                        */
+                       if (vop_dev->devfreq) {
+                               if (vop_dev->devfreq_event_dev)
+                                       devfreq_event_enable_edev(vop_dev->devfreq_event_dev);
+                               devfreq_resume_device(vop_dev->devfreq);
+                       }
+               } else if (open) {
+                       vop_early_resume(&vop_dev->driver);
+                       vop_dev->vop_switch_status = 0;
+                       wake_up(&vop_dev->wait_vop_switch_queue);
+                       /* if enable two vop, need to disable dmc */
+                       if (vop_dev->devfreq) {
+                               if (vop_dev->devfreq_event_dev)
+                                       devfreq_event_disable_edev(vop_dev->devfreq_event_dev);
+                               devfreq_suspend_device(vop_dev->devfreq);
+                       }
+                       dev_info(vop_dev->dev, "wake up from standby!\n");
+               }
+       } else if (vop_dev->prop == PRMRY) {
+               if ((open) && (!vop_dev->atv_layer_cnt)) {
+                       vop_dev->vop_switch_status = 0;
+                       wake_up(&vop_dev->wait_vop_switch_queue);
                }
        }
-       spin_unlock(&vop_dev->reg_lock);
 }
 
 static int vop_enable_irq(struct rk_lcdc_driver *dev_drv)
@@ -2069,12 +2265,38 @@ static int vop_enable_irq(struct rk_lcdc_driver *dev_drv)
        val = INTR_FS | INTR_LINE_FLAG0 | INTR_BUS_ERROR | INTR_LINE_FLAG1 |
                INTR_WIN0_EMPTY | INTR_WIN1_EMPTY | INTR_HWC_EMPTY |
                INTR_POST_BUF_EMPTY;
+       val |= val << 16;
 
-       vop_mask_writel(vop_dev, INTR_EN0, INTR_MASK, val);
+       vop_msk_reg(vop_dev, INTR_EN0, val);
 
        return 0;
 }
 
+static int dmc_notify(struct notifier_block *nb, unsigned long event,
+                     void *data)
+{
+       struct vop_device *vop = container_of(nb, struct vop_device, dmc_nb);
+
+       if (event == DEVFREQ_PRECHANGE) {
+
+               /*
+                * check if vop in enable or disable process,
+                * if yes, wait until it finish, use 200ms as
+                * timeout.
+                */
+               if (!wait_event_timeout(vop->wait_vop_switch_queue,
+                                       !vop->vop_switch_status, HZ / 5))
+                       dev_warn(vop->dev,
+                                "Timeout waiting for vop swtich status\n");
+               vop->dmc_in_process = 1;
+       } else if (event == DEVFREQ_POSTCHANGE) {
+               vop->dmc_in_process = 0;
+               wake_up(&vop->wait_dmc_queue);
+       }
+
+       return NOTIFY_OK;
+}
+
 static int vop_open(struct rk_lcdc_driver *dev_drv, int win_id,
                    bool open)
 {
@@ -2084,6 +2306,11 @@ static int vop_open(struct rk_lcdc_driver *dev_drv, int win_id,
        /* enable clk,when first layer open */
        if ((open) && (!vop_dev->atv_layer_cnt)) {
                /* rockchip_set_system_status(sys_status); */
+               if (!wait_event_timeout(vop_dev->wait_dmc_queue,
+                               !vop_dev->dmc_in_process, HZ / 5))
+                       dev_warn(vop_dev->dev,
+                                "Timeout waiting for dmc when vop enable\n");
+               vop_dev->vop_switch_status = 1;
                vop_pre_init(dev_drv);
                vop_clk_enable(vop_dev);
                vop_enable_irq(dev_drv);
@@ -2234,27 +2461,27 @@ static int vop_pan_display(struct rk_lcdc_driver *dev_drv, int win_id)
 
 static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen)
 {
-       u16 srcW;
-       u16 srcH;
-       u16 dstW;
-       u16 dstH;
-       u16 yrgb_srcW;
-       u16 yrgb_srcH;
-       u16 yrgb_dstW;
-       u16 yrgb_dstH;
-       u32 yrgb_vscalednmult;
-       u32 yrgb_xscl_factor;
-       u32 yrgb_yscl_factor;
+       u16 srcW = 0;
+       u16 srcH = 0;
+       u16 dstW = 0;
+       u16 dstH = 0;
+       u16 yrgb_srcW = 0;
+       u16 yrgb_srcH = 0;
+       u16 yrgb_dstW = 0;
+       u16 yrgb_dstH = 0;
+       u32 yrgb_vscalednmult = 0;
+       u32 yrgb_xscl_factor = 0;
+       u32 yrgb_yscl_factor = 0;
        u8 yrgb_vsd_bil_gt2 = 0;
        u8 yrgb_vsd_bil_gt4 = 0;
 
-       u16 cbcr_srcW;
-       u16 cbcr_srcH;
-       u16 cbcr_dstW;
-       u16 cbcr_dstH;
-       u32 cbcr_vscalednmult;
-       u32 cbcr_xscl_factor;
-       u32 cbcr_yscl_factor;
+       u16 cbcr_srcW = 0;
+       u16 cbcr_srcH = 0;
+       u16 cbcr_dstW = 0;
+       u16 cbcr_dstH = 0;
+       u32 cbcr_vscalednmult = 0;
+       u32 cbcr_xscl_factor = 0;
+       u32 cbcr_yscl_factor = 0;
        u8 cbcr_vsd_bil_gt2 = 0;
        u8 cbcr_vsd_bil_gt4 = 0;
        u8 yuv_fmt = 0;
@@ -2769,8 +2996,8 @@ static int dsp_y_pos(int mirror_en, struct rk_screen *screen,
 static int win_0_1_set_par(struct vop_device *vop_dev,
                           struct rk_screen *screen, struct rk_lcdc_win *win)
 {
-       u32 xact, yact, xvir, yvir, xpos, ypos;
-       u8 fmt_cfg = 0, swap_rb, swap_uv = 0;
+       u32 xact = 0, yact = 0, xvir = 0, yvir = 0, xpos = 0, ypos = 0;
+       u8 fmt_cfg = 0, swap_rb = 0, swap_uv = 0;
        char fmt[9] = "NULL";
 
        xpos = dsp_x_pos(win->xmirror, screen, &win->area[0]);
@@ -2923,7 +3150,7 @@ static int win_2_3_set_par(struct vop_device *vop_dev,
                           struct rk_screen *screen, struct rk_lcdc_win *win)
 {
        int i;
-       u8 fmt_cfg, swap_rb;
+       u8 fmt_cfg = 0, swap_rb = 0;
        char fmt[9] = "NULL";
 
        if (VOP_CHIP(vop_dev) == VOP_RK322X) {
@@ -2943,19 +3170,25 @@ static int win_2_3_set_par(struct vop_device *vop_dev,
                                fmt_cfg = 2;
                                swap_rb = 0;
                                win->fmt_10 = 0;
-                               win->area[0].fbdc_fmt_cfg = 0x05;
+                               win->area[0].fbdc_fmt_cfg = AFBDC_FMT_RGB565;
                                break;
                        case FBDC_ARGB_888:
+                               fmt_cfg = 0;
+                               swap_rb = 1;
+                               win->fmt_10 = 0;
+                               win->area[0].fbdc_fmt_cfg = AFBDC_FMT_U8U8U8U8;
+                               break;
+                       case FBDC_ABGR_888:
                                fmt_cfg = 0;
                                swap_rb = 0;
                                win->fmt_10 = 0;
-                               win->area[0].fbdc_fmt_cfg = 0x0c;
+                               win->area[0].fbdc_fmt_cfg = AFBDC_FMT_U8U8U8U8;
                                break;
                        case FBDC_RGBX_888:
                                fmt_cfg = 0;
                                swap_rb = 0;
                                win->fmt_10 = 0;
-                               win->area[0].fbdc_fmt_cfg = 0x3a;
+                               win->area[0].fbdc_fmt_cfg = AFBDC_FMT_U8U8U8U8;
                                break;
                        case ARGB888:
                                fmt_cfg = 0;
@@ -3013,8 +3246,8 @@ static int win_2_3_set_par(struct vop_device *vop_dev,
 static int hwc_set_par(struct vop_device *vop_dev,
                       struct rk_screen *screen, struct rk_lcdc_win *win)
 {
-       u32 xact, yact, xvir, yvir, xpos, ypos;
-       u8 fmt_cfg = 0, swap_rb;
+       u32 xact = 0, yact = 0, xvir = 0, yvir = 0, xpos = 0, ypos = 0;
+       u8 fmt_cfg = 0, swap_rb = 0;
        char fmt[9] = "NULL";
 
        xpos = win->area[0].xpos + screen->mode.left_margin +
@@ -3121,6 +3354,8 @@ static int vop_set_writeback(struct rk_lcdc_driver *dev_drv)
                return 0;
        }
        wb_data = &dev_drv->wb_data;
+       if ((wb_data->xsize == 0) || (wb_data->ysize == 0))
+               return 0;
 
        xsize = wb_data->xsize;
        ysize = wb_data->ysize;
@@ -3144,12 +3379,17 @@ static int vop_set_writeback(struct rk_lcdc_driver *dev_drv)
 
        switch (wb_data->data_format) {
        case ARGB888:
+       case ABGR888:
+       case XRGB888:
+       case XBGR888:
                fmt_cfg = 0;
                break;
        case RGB888:
+       case BGR888:
                fmt_cfg = 1;
                break;
        case RGB565:
+       case BGR565:
                fmt_cfg = 2;
                break;
        case YUV420:
@@ -3279,6 +3519,13 @@ static int vop_early_suspend(struct rk_lcdc_driver *dev_drv)
                vop_mask_writel(vop_dev, INTR_CLEAR0, INTR_MASK, INTR_MASK);
                vop_msk_reg(vop_dev, DSP_CTRL0, V_DSP_OUT_ZERO(1));
                vop_msk_reg(vop_dev, SYS_CTRL, V_VOP_STANDBY_EN(1));
+               if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+                       vop_msk_reg(vop_dev, WIN0_CTRL0, V_WIN0_EN(0));
+                       vop_msk_reg(vop_dev, WIN1_CTRL0, V_WIN1_EN(0));
+                       vop_msk_reg(vop_dev, WIN2_CTRL0, V_WIN2_EN(0));
+                       vop_msk_reg(vop_dev, WIN3_CTRL0, V_WIN3_EN(0));
+                       vop_msk_reg(vop_dev, AFBCD0_CTRL, V_VOP_FBDC_EN(0));
+               }
                vop_cfg_done(vop_dev);
 
                if (dev_drv->iommu_enabled && dev_drv->mmu_dev) {
@@ -3314,7 +3561,6 @@ static int vop_early_resume(struct rk_lcdc_driver *dev_drv)
        spin_lock(&vop_dev->reg_lock);
 
        vop_msk_reg(vop_dev, DSP_CTRL0, V_DSP_OUT_ZERO(0));
-       vop_msk_reg(vop_dev, SYS_CTRL, V_VOP_STANDBY_EN(0));
        vop_msk_reg(vop_dev, DSP_CTRL0, V_DSP_BLANK_EN(0));
        vop_cfg_done(vop_dev);
        spin_unlock(&vop_dev->reg_lock);
@@ -3327,6 +3573,11 @@ static int vop_early_resume(struct rk_lcdc_driver *dev_drv)
                rockchip_iovmm_activate(dev_drv->dev);
        }
 
+       spin_lock(&vop_dev->reg_lock);
+       vop_msk_reg(vop_dev, SYS_CTRL, V_VOP_STANDBY_EN(0));
+       vop_cfg_done(vop_dev);
+       spin_unlock(&vop_dev->reg_lock);
+
        dev_drv->suspend_flag = 0;
 
        if (dev_drv->trsm_ops && dev_drv->trsm_ops->enable)
@@ -3432,10 +3683,10 @@ static int vop_ovl_mgr(struct rk_lcdc_driver *dev_drv, int swap, bool set)
        struct vop_device *vop_dev =
            container_of(dev_drv, struct vop_device, driver);
        struct rk_lcdc_win *win = NULL;
-       int i, ovl;
+       int i, ovl = 0;
        u64 val;
        int z_order_num = 0;
-       int layer0_sel, layer1_sel, layer2_sel, layer3_sel;
+       int layer0_sel = 0, layer1_sel = 1, layer2_sel = 2, layer3_sel = 3;
 
        if (swap == 0) {
                for (i = 0; i < dev_drv->lcdc_win_num; i++) {
@@ -3779,7 +4030,7 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv,
        spin_unlock(&vop_dev->reg_lock);
        size += snprintf(dsp_buf, 80,
                "z-order:\n  win[%d]\n  win[%d]\n  win[%d]\n  win[%d]\n",
-               layer1_sel, layer0_sel, layer2_sel, layer3_sel);
+               layer3_sel, layer2_sel, layer1_sel, layer0_sel);
        strcat(buf, dsp_buf);
        memset(dsp_buf, 0, sizeof(dsp_buf));
        /* win0 */
@@ -3956,40 +4207,68 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv,
 static int vop_fps_mgr(struct rk_lcdc_driver *dev_drv, int fps, bool set)
 {
        struct vop_device *vop_dev =
-           container_of(dev_drv, struct vop_device, driver);
-       struct rk_screen *screen = dev_drv->cur_screen;
-       u64 ft = 0;
-       u32 dotclk;
+               container_of(dev_drv, struct vop_device, driver);
+       struct rk_fb_vsync *vsync = &dev_drv->vsync_info;
+       int step_fps, old_fps;
+       u32 h_total, v_total;
+       unsigned long dclk;
+       u64 val;
        int ret;
-       u32 pixclock;
-       u32 x_total, y_total;
 
-       if (set) {
-               if (fps == 0) {
-                       dev_info(dev_drv->dev, "unsupport set fps=0\n");
-                       return 0;
+       dclk = clk_get_rate(vop_dev->dclk);
+
+       spin_lock(&vop_dev->reg_lock);
+
+       if (!vop_dev->clk_on) {
+               spin_unlock(&vop_dev->reg_lock);
+               return 0;
+       }
+
+       val = vop_readl(vop_dev, DSP_HTOTAL_HS_END);
+       h_total = (val & MASK(DSP_HTOTAL)) >> 16;
+
+       val = vop_readl(vop_dev, DSP_VTOTAL_VS_END);
+       v_total = (val & MASK(DSP_VTOTAL)) >> 16;
+
+       spin_unlock(&vop_dev->reg_lock);
+
+       old_fps = div_u64(dclk, v_total * h_total);
+
+       if (!set)
+               return old_fps;
+
+       /*
+        * Direct change fps to dest fps would may screen flash,
+        * Every frame change one step fps is safe, screen flash
+        * disappear.
+        */
+       step_fps = old_fps;
+       while (step_fps != fps) {
+               ktime_t timestamp = vsync->timestamp;
+
+               if (step_fps > fps)
+                       step_fps--;
+               else
+                       step_fps++;
+               spin_lock(&vop_dev->reg_lock);
+               if (!vop_dev->clk_on) {
+                       spin_unlock(&vop_dev->reg_lock);
+                       break;
                }
-               ft = div_u64(1000000000000llu, fps);
-               x_total =
-                   screen->mode.upper_margin + screen->mode.lower_margin +
-                   screen->mode.yres + screen->mode.vsync_len;
-               y_total =
-                   screen->mode.left_margin + screen->mode.right_margin +
-                   screen->mode.xres + screen->mode.hsync_len;
-               dev_drv->pixclock = div_u64(ft, x_total * y_total);
-               dotclk = div_u64(1000000000000llu, dev_drv->pixclock);
-               ret = clk_set_rate(vop_dev->dclk, dotclk);
-       }
-
-       pixclock = div_u64(1000000000000llu, clk_get_rate(vop_dev->dclk));
-       vop_dev->pixclock = pixclock;
-       dev_drv->pixclock = vop_dev->pixclock;
-       fps = rk_fb_calc_fps(screen, pixclock);
-       screen->ft = 1000 / fps;        /*one frame time in ms */
-
-       if (set)
-               dev_info(dev_drv->dev, "%s:dclk:%lu,fps:%d\n", __func__,
-                        clk_get_rate(vop_dev->dclk), fps);
+               h_total = div_u64(dclk, step_fps * v_total);
+               val = V_DSP_HTOTAL(h_total);
+               vop_msk_reg(vop_dev, DSP_HTOTAL_HS_END, val);
+               vop_cfg_done(vop_dev);
+               spin_unlock(&vop_dev->reg_lock);
+
+               ret = wait_event_interruptible_timeout(vsync->wait,
+                       !ktime_equal(timestamp, vsync->timestamp) &&
+                       (vsync->active > 0 || vsync->irq_stop),
+                       msecs_to_jiffies(50));
+       }
+
+       dev_info(dev_drv->dev, "%s:dclk:%lu, htotal=%d, vtatol=%d, fps:%d\n",
+                __func__, dclk, h_total, v_total, fps);
 
        return fps;
 }
@@ -4042,7 +4321,10 @@ static int vop_config_done(struct rk_lcdc_driver *dev_drv)
        vop_msk_reg(vop_dev, SYS_CTRL, V_VOP_STANDBY_EN(vop_dev->standby));
        for (i = 0; i < dev_drv->lcdc_win_num; i++) {
                win = dev_drv->win[i];
+               vop_alpha_cfg(dev_drv, i);
                fbdc_en |= win->area[0].fbdc_en;
+               vop_dev->atv_layer_cnt &= ~(1 << win->id);
+               vop_dev->atv_layer_cnt |= (win->state << win->id);
                if ((win->state == 0) && (win->last_state == 1)) {
                        switch (win->id) {
                        case 0:
@@ -4125,7 +4407,7 @@ static int vop_set_irq_to_cpu(struct rk_lcdc_driver *dev_drv, int enable)
        if (enable)
                enable_irq(vop_dev->irq);
        else
-               disable_irq(vop_dev->irq);
+               disable_irq_nosync(vop_dev->irq);
        return 0;
 }
 
@@ -4204,7 +4486,7 @@ static int vop_get_bcsh_hue(struct rk_lcdc_driver *dev_drv, bcsh_hue_mode mode)
 {
        struct vop_device *vop_dev =
            container_of(dev_drv, struct vop_device, driver);
-       u32 val;
+       u32 val = 0;
 
        spin_lock(&vop_dev->reg_lock);
        if (vop_dev->clk_on) {
@@ -4234,6 +4516,9 @@ static int vop_set_dsp_cabc(struct rk_lcdc_driver *dev_drv, int mode,
        struct rk_screen *screen = dev_drv->cur_screen;
        u32 total_pixel, calc_pixel, stage_up, stage_down;
        u32 pixel_num, global_dn;
+       u64 val = 0;
+       ktime_t timestamp;
+       int ret = 0;
 
        if (!vop_dev->cabc_lut_addr_base) {
                pr_err("vop chip[%d] not supoort cabc\n", VOP_CHIP(vop_dev));
@@ -4245,17 +4530,23 @@ static int vop_set_dsp_cabc(struct rk_lcdc_driver *dev_drv, int mode,
                return 0;
        }
 
-       dev_drv->cabc_mode = mode;
-       if (!dev_drv->cabc_mode) {
-               spin_lock(&vop_dev->reg_lock);
-               if (vop_dev->clk_on) {
-                       vop_msk_reg(vop_dev, CABC_CTRL0,
-                                   V_CABC_EN(0) | V_CABC_HANDLE_EN(0));
-                       vop_cfg_done(vop_dev);
+       if (!mode) {
+               if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+                       calc = 0;
+                       up = 256;
+                       down = 255;
+                       global = 0;
+               } else {
+                       spin_lock(&vop_dev->reg_lock);
+                       if (vop_dev->clk_on) {
+                               vop_msk_reg(vop_dev, CABC_CTRL0,
+                                           V_CABC_EN(0) | V_CABC_HANDLE_EN(0));
+                               vop_cfg_done(vop_dev);
+                       }
+                       pr_info("mode = 0, close cabc\n");
+                       spin_unlock(&vop_dev->reg_lock);
+                       return 0;
                }
-               pr_info("mode = 0, close cabc\n");
-               spin_unlock(&vop_dev->reg_lock);
-               return 0;
        }
 
        total_pixel = screen->mode.xres * screen->mode.yres;
@@ -4269,10 +4560,7 @@ static int vop_set_dsp_cabc(struct rk_lcdc_driver *dev_drv, int mode,
 
        spin_lock(&vop_dev->reg_lock);
        if (vop_dev->clk_on) {
-               u64 val = 0;
-
-               val = V_CABC_EN(1) | V_CABC_HANDLE_EN(1) |
-                       V_PWM_CONFIG_MODE(STAGE_BY_STAGE) |
+               val = V_PWM_CONFIG_MODE(STAGE_BY_STAGE) |
                        V_CABC_CALC_PIXEL_NUM(calc_pixel);
                vop_msk_reg(vop_dev, CABC_CTRL0, val);
 
@@ -4292,6 +4580,23 @@ static int vop_set_dsp_cabc(struct rk_lcdc_driver *dev_drv, int mode,
        }
        spin_unlock(&vop_dev->reg_lock);
 
+       timestamp = dev_drv->vsync_info.timestamp;
+       ret = wait_event_interruptible_timeout(dev_drv->vsync_info.wait,
+                       !ktime_equal(timestamp, dev_drv->vsync_info.timestamp),
+                       msecs_to_jiffies(50));
+       if (ret < 0)
+               return ret;
+       else if (ret == 0)
+               pr_err("%s wait vsync time out\n", __func__);
+
+       spin_lock(&vop_dev->reg_lock);
+       if (vop_dev->clk_on) {
+               val = V_CABC_EN(1) | V_CABC_HANDLE_EN(1);
+               vop_msk_reg(vop_dev, CABC_CTRL0, val);
+               vop_cfg_done(vop_dev);
+       }
+       spin_unlock(&vop_dev->reg_lock);
+
        return 0;
 }
 
@@ -4318,7 +4623,7 @@ static int vop_set_bcsh_bcs(struct rk_lcdc_driver *dev_drv,
 {
        struct vop_device *vop_dev =
            container_of(dev_drv, struct vop_device, driver);
-       u64 val;
+       u64 val = 0;
 
        spin_lock(&vop_dev->reg_lock);
        if (vop_dev->clk_on) {
@@ -4354,7 +4659,7 @@ static int vop_get_bcsh_bcs(struct rk_lcdc_driver *dev_drv, bcsh_bcs_mode mode)
 {
        struct vop_device *vop_dev =
            container_of(dev_drv, struct vop_device, driver);
-       u64 val;
+       u64 val = 0;
 
        spin_lock(&vop_dev->reg_lock);
        if (vop_dev->clk_on) {
@@ -4511,6 +4816,27 @@ static int vop_set_overscan(struct rk_lcdc_driver *dev_drv,
        return 0;
 }
 
+static int vop_extern_func(struct rk_lcdc_driver *dev_drv, int cmd)
+{
+       struct vop_device *vop_dev =
+           container_of(dev_drv, struct vop_device, driver);
+
+       if (unlikely(!vop_dev->clk_on)) {
+               pr_info("%s,clk_on = %d\n", __func__, vop_dev->clk_on);
+               return 0;
+       }
+
+       switch (cmd) {
+       case UPDATE_CABC_PWM:
+               vop_cfg_done(vop_dev);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static struct rk_lcdc_drv_ops lcdc_drv_ops = {
        .open = vop_open,
        .win_direct_en = vop_win_direct_en,
@@ -4553,6 +4879,7 @@ static struct rk_lcdc_drv_ops lcdc_drv_ops = {
        .backlight_close = vop_backlight_close,
        .mmu_en    = vop_mmu_en,
        .set_overscan   = vop_set_overscan,
+       .extern_func    = vop_extern_func,
 };
 
 static irqreturn_t vop_isr(int irq, void *dev_id)
@@ -4574,13 +4901,14 @@ static irqreturn_t vop_isr(int irq, void *dev_id)
 
        if (intr_status & INTR_FS) {
                timestamp = ktime_get();
-               if (vop_dev->wb_on) {
+               if (vop_dev->driver.wb_data.state) {
                        u32 wb_status;
 
                        spin_lock_irqsave(&vop_dev->irq_lock, flags);
                        wb_status = vop_read_bit(vop_dev, WB_CTRL0, V_WB_EN(0));
+
                        if (wb_status)
-                               vop_set_bit(vop_dev, WB_CTRL0, V_WB_EN(0));
+                               vop_clr_bit(vop_dev, WB_CTRL0, V_WB_EN(0));
 
                        vop_cfg_done(vop_dev);
                        vop_dev->driver.wb_data.state = 0;
@@ -4662,6 +4990,11 @@ static int vop_parse_dt(struct vop_device *vop_dev)
        else
                dev_drv->rotate_mode = val;
 
+       if (of_property_read_u32(np, "rockchip,cabc_mode", &val))
+               dev_drv->cabc_mode = 0; /* default set close cabc */
+       else
+               dev_drv->cabc_mode = val;
+
        if (of_property_read_u32(np, "rockchip,pwr18", &val))
                /*default set it as 3.xv power supply */
                vop_dev->pwr18 = false;
@@ -4705,9 +5038,85 @@ static int vop_parse_dt(struct vop_device *vop_dev)
                dev_drv->iommu_enabled = 0;
        else
                dev_drv->iommu_enabled = val;
+
+       if (of_property_read_u32(np, "rockchip,dsp_mode", &val))
+               dev_drv->dsp_mode = DEFAULT_MODE;
+       else
+               dev_drv->dsp_mode = val;
+
+       return 0;
+}
+
+static struct platform_device *rk322x_pdev;
+
+int vop_register_dmc(void)
+{
+       struct platform_device *pdev = rk322x_pdev;
+       struct vop_device *vop_dev;
+       struct device *dev = &pdev->dev;
+       struct devfreq *devfreq;
+       struct devfreq_event_dev *event_dev;
+
+       if (!pdev)
+               return -ENODEV;
+
+       vop_dev = platform_get_drvdata(pdev);;
+       if (!vop_dev)
+               return -ENODEV;
+
+       dev = &pdev->dev;
+       devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
+       if (IS_ERR(devfreq)) {
+               dev_err(vop_dev->dev, "fail to get devfreq for dmc\n");
+               return -ENODEV;
+       }
+
+       vop_dev->devfreq = devfreq;
+       vop_dev->dmc_nb.notifier_call = dmc_notify;
+       devfreq_register_notifier(vop_dev->devfreq, &vop_dev->dmc_nb,
+                                 DEVFREQ_TRANSITION_NOTIFIER);
+
+       event_dev = devfreq_event_get_edev_by_phandle(vop_dev->devfreq->dev.parent,
+                                                     0);
+       if (IS_ERR(event_dev)) {
+               dev_err(vop_dev->dev, "fail to get edev for dmc\n");
+               return -ENODEV;
+       }
+
+       vop_dev->devfreq_event_dev = event_dev;
+       return 0;
+}
+
+static int vop_wms_refresh(struct rk_lcdc_driver *dev_drv)
+{
+       struct vop_device *vop_dev =
+           container_of(dev_drv, struct vop_device, driver);
+
+       if (unlikely(!vop_dev->clk_on)) {
+               dev_info_ratelimited(vop_dev->dev, "%s,clk_on = %d\n",
+                                    __func__, vop_dev->clk_on);
+               return 0;
+       }
+       vop_msk_reg_nobak(vop_dev, SYS_CTRL, V_EDPI_WMS_FS(1));
+       vop_msk_reg(vop_dev, SYS_CTRL, V_EDPI_WMS_MODE(0));
+       vop_msk_reg(vop_dev, SYS_CTRL, V_EDPI_WMS_MODE(1));
+
+       if (dev_drv->trsm_ops && dev_drv->trsm_ops->refresh)
+               dev_drv->trsm_ops->refresh(0, 0, dev_drv->cur_screen->mode.xres,
+                                          dev_drv->cur_screen->mode.yres);
+
        return 0;
 }
 
+static irqreturn_t te_irq_handle(int irq, void *dev_id)
+{
+       struct rk_lcdc_driver *dev_drv  = (struct rk_lcdc_driver *)dev_id;
+
+       vop_wms_refresh(dev_drv);
+
+       return IRQ_HANDLED;
+}
+
 static int vop_probe(struct platform_device *pdev)
 {
        struct vop_device *vop_dev = NULL;
@@ -4718,6 +5127,7 @@ static int vop_probe(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        int prop;
        int ret = 0;
+       int te_pin;
 
        /* if the primary lcdc has not registered ,the extend
         * lcdc register later
@@ -4737,10 +5147,15 @@ static int vop_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, vop_dev);
        vop_dev->dev = dev;
        vop_parse_dt(vop_dev);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
+       /* enable power domain */
+       pm_runtime_enable(dev);
+#endif
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        vop_dev->reg_phy_base = res->start;
        vop_dev->len = resource_size(res);
-       vop_dev->regs = devm_ioremap_resource(dev, res);
+       vop_dev->regs = devm_ioremap(&pdev->dev, res->start,
+                                    resource_size(res));
        if (IS_ERR(vop_dev->regs))
                return PTR_ERR(vop_dev->regs);
 
@@ -4786,17 +5201,69 @@ static int vop_probe(struct platform_device *pdev)
                        vop_dev->irq, ret);
                return ret;
        }
-       if (dev_drv->iommu_enabled)
-               strcpy(dev_drv->mmu_dts_name, VOP_IOMMU_COMPATIBLE_NAME);
+       if (dev_drv->iommu_enabled) {
+               if (VOP_CHIP(vop_dev) == VOP_RK322X) {
+                       strcpy(dev_drv->mmu_dts_name,
+                              VOP_IOMMU_COMPATIBLE_NAME);
+               } else {
+                       if (vop_dev->id == 0)
+                               strcpy(dev_drv->mmu_dts_name,
+                                      VOPB_IOMMU_COMPATIBLE_NAME);
+                       else
+                               strcpy(dev_drv->mmu_dts_name,
+                                      VOPL_IOMMU_COMPATIBLE_NAME);
+               }
+       }
+       if (VOP_CHIP(vop_dev) == VOP_RK3399)
+               dev_drv->property.feature |= SUPPORT_WRITE_BACK | SUPPORT_AFBDC;
+       dev_drv->property.feature |= SUPPORT_VOP_IDENTIFY |
+                                       SUPPORT_YUV420_OUTPUT;
+       dev_drv->property.max_output_x = 4096;
+       dev_drv->property.max_output_y = 2160;
+
+       init_waitqueue_head(&vop_dev->wait_vop_switch_queue);
+       vop_dev->vop_switch_status = 0;
+       init_waitqueue_head(&vop_dev->wait_dmc_queue);
+       vop_dev->dmc_in_process = 0;
+
        ret = rk_fb_register(dev_drv, vop_dev->data->win, vop_dev->id);
        if (ret < 0) {
                dev_err(dev, "register fb for lcdc%d failed!\n", vop_dev->id);
                return ret;
        }
+
+       if ((VOP_CHIP(vop_dev) == VOP_RK3399) && (vop_dev->id == 1)) {
+               dev_drv->win[1]->property.feature &= ~SUPPORT_HW_EXIST;
+               dev_drv->win[3]->property.feature &= ~SUPPORT_HW_EXIST;
+       }
+
        vop_dev->screen = dev_drv->screen0;
        dev_info(dev, "lcdc%d probe ok, iommu %s\n",
                 vop_dev->id, dev_drv->iommu_enabled ? "enabled" : "disabled");
 
+       rk322x_pdev = pdev;
+
+       if (dev_drv->cur_screen->refresh_mode == SCREEN_CMD_MODE) {
+               te_pin = of_get_named_gpio_flags(np, "te-gpio", 0, NULL);
+               if (IS_ERR_VALUE(te_pin)) {
+                       dev_err(dev, "define te pin for cmd mode!\n");
+                       return 0;
+               }
+               ret = devm_gpio_request(dev, te_pin, "vop-te-gpio");
+               if (ret) {
+                       dev_err(dev, "request gpio %d failed\n", te_pin);
+                       return 0;
+               }
+               gpio_direction_input(te_pin);
+               dev_drv->te_irq = gpio_to_irq(te_pin);
+               ret = devm_request_threaded_irq(dev,
+                                               dev_drv->te_irq,
+                                               NULL, te_irq_handle,
+                                               IRQ_TYPE_EDGE_FALLING | IRQF_ONESHOT,
+                                               "te_irq", dev_drv);
+               if (ret < 0)
+                       dev_err(dev, "request te irq failed, ret: %d\n", ret);
+       }
        return 0;
 }
 
@@ -4818,6 +5285,9 @@ static void vop_shutdown(struct platform_device *pdev)
        vop_deint(vop_dev);
 
        vop_clk_disable(vop_dev);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
+       pm_runtime_disable(vop_dev->dev);
+#endif
        rk_disp_pwr_disable(dev_drv);
 }