rk fb: add support yuv format rotate through rga module that used for HDMI
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / rk_fb.c
index 7545d2389f5ad325c82ba90f7a20abd77edeca59..39f7d69d31582af6fdfe6dec0ef35e7ef0da410d 100755 (executable)
@@ -231,7 +231,6 @@ int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
        enum of_gpio_flags flags;
        u32 val = 0;
        u32 debug = 0;
-       u32 mirror = 0;
        int ret;
 
        INIT_LIST_HEAD(&dev_drv->pwrlist_head);
@@ -273,22 +272,6 @@ int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
                list_add_tail(&pwr_ctr->list, &dev_drv->pwrlist_head);
        }
 
-       of_property_read_u32(root, "rockchip,mirror", &mirror);
-
-       if (mirror == NO_MIRROR) {
-               dev_drv->screen0->x_mirror = 0;
-               dev_drv->screen0->y_mirror = 0;
-       } else if (mirror == X_MIRROR) {
-               dev_drv->screen0->x_mirror = 1;
-               dev_drv->screen0->y_mirror = 0;
-       } else if (mirror == Y_MIRROR) {
-               dev_drv->screen0->x_mirror = 0;
-               dev_drv->screen0->y_mirror = 1;
-       } else if (mirror == X_Y_MIRROR) {
-               dev_drv->screen0->x_mirror = 1;
-               dev_drv->screen0->y_mirror = 1;
-       }
-
        of_property_read_u32(root, "rockchip,debug", &debug);
 
        if (debug) {
@@ -512,27 +495,115 @@ static struct rk_lcdc_driver *rk_get_prmry_lcdc_drv(void)
        return dev_drv;
 }
 
-int rk_fb_get_prmry_screen_ft(void)
+/*
+ * get one frame time of the prmry screen, unit: us
+ */
+u32 rk_fb_get_prmry_screen_ft(void)
 {
        struct rk_lcdc_driver *dev_drv = rk_get_prmry_lcdc_drv();
-       uint32_t htotal, vtotal, pix_total, ft_us, pixclock_ns;
+       uint32_t htotal, vtotal, pixclock_ps;
+       u64 pix_total, ft_us;
 
        if (unlikely(!dev_drv))
                return 0;
 
-       pixclock_ns = dev_drv->pixclock / 1000;
+       pixclock_ps = dev_drv->pixclock;
 
-       htotal = (dev_drv->cur_screen->mode.upper_margin +
+       vtotal = (dev_drv->cur_screen->mode.upper_margin +
                 dev_drv->cur_screen->mode.lower_margin +
                 dev_drv->cur_screen->mode.yres +
                 dev_drv->cur_screen->mode.vsync_len);
-       vtotal = (dev_drv->cur_screen->mode.left_margin +
+       htotal = (dev_drv->cur_screen->mode.left_margin +
                 dev_drv->cur_screen->mode.right_margin +
                 dev_drv->cur_screen->mode.xres +
                 dev_drv->cur_screen->mode.hsync_len);
-       pix_total = htotal * vtotal / 1000;
-       ft_us = pix_total * pixclock_ns;
-       return ft_us;
+       pix_total = htotal * vtotal;
+       ft_us = pix_total * pixclock_ps;
+       do_div(ft_us, 1000000);
+       if (dev_drv->frame_time.ft == 0)
+               dev_drv->frame_time.ft = ft_us;
+
+       ft_us = dev_drv->frame_time.framedone_t - dev_drv->frame_time.last_framedone_t;
+       do_div(ft_us, 1000);
+       ft_us = min(dev_drv->frame_time.ft, (u32)ft_us);
+       if (ft_us != 0)
+               dev_drv->frame_time.ft = ft_us;
+
+       return dev_drv->frame_time.ft;
+}
+
+/*
+ * get the vblanking time of the prmry screen, unit: us
+ */
+u32 rk_fb_get_prmry_screen_vbt(void)
+{
+       struct rk_lcdc_driver *dev_drv = rk_get_prmry_lcdc_drv();
+       uint32_t htotal, vblank, pixclock_ps;
+       u64 pix_blank, vbt_us;
+
+       if (unlikely(!dev_drv))
+               return 0;
+
+       pixclock_ps = dev_drv->pixclock;
+
+       htotal = (dev_drv->cur_screen->mode.left_margin +
+                dev_drv->cur_screen->mode.right_margin +
+                dev_drv->cur_screen->mode.xres +
+                dev_drv->cur_screen->mode.hsync_len);
+       vblank = (dev_drv->cur_screen->mode.upper_margin +
+                dev_drv->cur_screen->mode.lower_margin +
+                dev_drv->cur_screen->mode.vsync_len);
+       pix_blank = htotal * vblank;
+       vbt_us = pix_blank * pixclock_ps;
+       do_div(vbt_us, 1000000);
+       return (u32)vbt_us;
+}
+
+/*
+ * get the frame done time of the prmry screen, unit: us
+ */
+u64 rk_fb_get_prmry_screen_framedone_t(void)
+{
+       struct rk_lcdc_driver *dev_drv = rk_get_prmry_lcdc_drv();
+
+       if (unlikely(!dev_drv))
+               return 0;
+       else
+               return dev_drv->frame_time.framedone_t;
+}
+
+/*
+ * set prmry screen status
+ */
+int rk_fb_set_prmry_screen_status(int status)
+{
+       struct rk_lcdc_driver *dev_drv = rk_get_prmry_lcdc_drv();
+       struct rk_screen *screen;
+
+       if (unlikely(!dev_drv))
+               return 0;
+
+       screen = dev_drv->cur_screen;
+       switch (status) {
+       case SCREEN_PREPARE_DDR_CHANGE:
+               if (screen->type == SCREEN_MIPI
+                       || screen->type == SCREEN_DUAL_MIPI) {
+                       if (dev_drv->trsm_ops->dsp_pwr_off)
+                               dev_drv->trsm_ops->dsp_pwr_off();
+               }
+               break;
+       case SCREEN_UNPREPARE_DDR_CHANGE:
+               if (screen->type == SCREEN_MIPI
+                       || screen->type == SCREEN_DUAL_MIPI) {
+                       if (dev_drv->trsm_ops->dsp_pwr_on)
+                               dev_drv->trsm_ops->dsp_pwr_on();
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
 }
 
 static struct rk_lcdc_driver *rk_get_extend_lcdc_drv(void)
@@ -559,7 +630,11 @@ static struct rk_lcdc_driver *rk_get_extend_lcdc_drv(void)
 u32 rk_fb_get_prmry_screen_pixclock(void)
 {
        struct rk_lcdc_driver *dev_drv = rk_get_prmry_lcdc_drv();
-       return dev_drv->pixclock;
+
+       if (unlikely(!dev_drv))
+               return 0;
+       else
+               return dev_drv->pixclock;
 }
 
 int rk_fb_poll_prmry_screen_vblank(void)
@@ -591,12 +666,10 @@ bool rk_fb_poll_wait_frame_complete(void)
                }
                return false;
        }
-
        while (!(rk_fb_poll_prmry_screen_vblank() == RK_LF_STATUS_FR) && --timeout)
                ;
        while (!(rk_fb_poll_prmry_screen_vblank() == RK_LF_STATUS_FC) && --timeout)
                ;
-
        if (likely(dev_drv)) {
                if (dev_drv->ops->set_irq_to_cpu)
                        dev_drv->ops->set_irq_to_cpu(dev_drv, 1);
@@ -670,6 +743,7 @@ static int rk_fb_close(struct fb_info *info, int user)
        return 0;
 }
 
+
 #if defined(CONFIG_RK29_IPP)
 static int get_ipp_format(int fmt)
 {
@@ -749,21 +823,23 @@ static void fb_copy_by_ipp(struct fb_info *dst_info,
        int dst_w, dst_h, dst_vir_w;
        int ipp_fmt;
        u8 data_format = (dst_info->var.nonstd) & 0xff;
+       struct rk_lcdc_driver *ext_dev_drv =
+                       (struct rk_lcdc_driver *)dst_info->par;
+       u16 orientation = ext_dev_drv->rotate_mode;
 
        memset(&ipp_req, 0, sizeof(struct rk29_ipp_req));
-#if defined(CONFIG_FB_ROTATE)
-       int orientation = 270 - CONFIG_ROTATE_ORIENTATION;
+
        switch (orientation) {
        case 0:
                rotation = IPP_ROT_0;
                break;
-       case 90:
+       case ROTATE_90:
                rotation = IPP_ROT_90;
                break;
-       case 180:
+       case ROTATE_180:
                rotation = IPP_ROT_180;
                break;
-       case 270:
+       case ROTATE_270:
                rotation = IPP_ROT_270;
                break;
        default:
@@ -771,7 +847,6 @@ static void fb_copy_by_ipp(struct fb_info *dst_info,
                break;
 
        }
-#endif
 
        dst_w = dst_info->var.xres;
        dst_h = dst_info->var.yres;
@@ -864,23 +939,17 @@ static void rga_win_check(struct rk_lcdc_win *dst_win,
 }
 
 static void win_copy_by_rga(struct rk_lcdc_win *dst_win,
-                           struct rk_lcdc_win *src_win)
+                           struct rk_lcdc_win *src_win, u16 orientation)
 {
-       struct rk_fb *rk_fb = platform_get_drvdata(fb_pdev);
        struct rga_req Rga_Request;
        long ret = 0;
        /* int fd = 0; */
-#if defined(CONFIG_FB_ROTATE)
-       int orientation = 0;
-#endif
 
        memset(&Rga_Request, 0, sizeof(Rga_Request));
        rga_win_check(dst_win, src_win);
 
-#if defined(CONFIG_FB_ROTATE)
-       orientation = 270 - CONFIG_ROTATE_ORIENTATION;
        switch (orientation) {
-       case 90:
+       case ROTATE_90:
                Rga_Request.rotate_mode = 1;
                Rga_Request.sina = 65536;
                Rga_Request.cosa = 0;
@@ -889,7 +958,7 @@ static void win_copy_by_rga(struct rk_lcdc_win *dst_win,
                Rga_Request.dst.x_offset = dst_win->area[0].xact - 1;
                Rga_Request.dst.y_offset = 0;
                break;
-       case 180:
+       case ROTATE_180:
                Rga_Request.rotate_mode = 1;
                Rga_Request.sina = 0;
                Rga_Request.cosa = -65536;
@@ -898,13 +967,13 @@ static void win_copy_by_rga(struct rk_lcdc_win *dst_win,
                Rga_Request.dst.x_offset = dst_win->area[0].xact - 1;
                Rga_Request.dst.y_offset = dst_win->area[0].yact - 1;
                break;
-       case 270:
+       case ROTATE_270:
                Rga_Request.rotate_mode = 1;
                Rga_Request.sina = -65536;
                Rga_Request.cosa = 0;
                Rga_Request.dst.act_w = dst_win->area[0].yact;
                Rga_Request.dst.act_h = dst_win->area[0].xact;
-               Rga_Request.dst.x_offset = dst_win->area[0].xact - 1;
+               Rga_Request.dst.x_offset = 0;
                Rga_Request.dst.y_offset = dst_win->area[0].yact - 1;
                break;
        default:
@@ -915,7 +984,6 @@ static void win_copy_by_rga(struct rk_lcdc_win *dst_win,
                Rga_Request.dst.y_offset = dst_win->area[0].yact - 1;
                break;
        }
-#endif
 
 #if defined(CONFIG_ROCKCHIP_RGA)
        Rga_Request.src.yrgb_addr =
@@ -923,8 +991,6 @@ static void win_copy_by_rga(struct rk_lcdc_win *dst_win,
        Rga_Request.src.uv_addr = 0;
        Rga_Request.src.v_addr = 0;
 
-       dst_win->area[0].smem_start =
-           rk_fb->fb[rk_fb->num_fb >> 1]->fix.smem_start;
        Rga_Request.dst.yrgb_addr =
            dst_win->area[0].smem_start + dst_win->area[0].y_offset;
        Rga_Request.dst.uv_addr = 0;
@@ -941,8 +1007,6 @@ static void win_copy_by_rga(struct rk_lcdc_win *dst_win,
            src_win->area[0].smem_start + src_win->area[0].y_offset;
        Rga_Request.src.v_addr = 0;
 
-       dst_win->area[0].smem_start =
-           rk_fb->fb[rk_fb->num_fb >> 1]->fix.smem_start;
        Rga_Request.dst.yrgb_addr = 0;
        Rga_Request.dst.uv_addr =
            dst_win->area[0].smem_start + dst_win->area[0].y_offset;
@@ -998,12 +1062,11 @@ static void fb_copy_by_rga(struct fb_info *dst_info, struct fb_info *src_info,
            ext_dev_drv->ops->fb_get_win_id(ext_dev_drv, dst_info->fix.id);
        dst_win = ext_dev_drv->win[ext_win_id];
 
-       win_copy_by_rga(dst_win, src_win);
+       win_copy_by_rga(dst_win, src_win, ext_dev_drv->rotate_mode);
 }
 
 #endif
 
-#if defined(CONFIG_FB_ROTATE) || !defined(CONFIG_THREE_FB_BUFFER)
 static int rk_fb_rotate(struct fb_info *dst_info,
                        struct fb_info *src_info, int offset)
 {
@@ -1018,16 +1081,16 @@ static int rk_fb_rotate(struct fb_info *dst_info,
 }
 
 static int rk_fb_win_rotate(struct rk_lcdc_win *dst_win,
-                           struct rk_lcdc_win *src_win)
+                           struct rk_lcdc_win *src_win, u16 rotate)
 {
 #if defined(CONFIG_ROCKCHIP_RGA) || defined(CONFIG_ROCKCHIP_RGA2)
-       win_copy_by_rga(dst_win, src_win);
+       win_copy_by_rga(dst_win, src_win, rotate);
 #else
        return -1;
 #endif
        return 0;
 }
-#endif
+
 
 static int rk_fb_pan_display(struct fb_var_screeninfo *var,
                             struct fb_info *info)
@@ -1170,12 +1233,15 @@ static int rk_fb_pan_display(struct fb_var_screeninfo *var,
        if (rk_fb->disp_mode == DUAL) {
                if (extend_win->state && (hdmi_switch_complete)) {
                        extend_win->area[0].y_offset = win->area[0].y_offset;
-#if defined(CONFIG_FB_ROTATE) || !defined(CONFIG_THREE_FB_BUFFER)
-                       rk_fb_rotate(extend_info, info, win->area[0].y_offset);
-#else
-                       extend_win->area[0].smem_start = win->area[0].smem_start;
-                       extend_win->area[0].cbr_start = win->area[0].cbr_start;
-#endif
+                       if (extend_dev_drv->rotate_mode > X_Y_MIRROR) {
+                               rk_fb_rotate(extend_info, info,
+                                               win->area[0].y_offset);
+                       } else {
+                               extend_win->area[0].smem_start =
+                                               win->area[0].smem_start;
+                               extend_win->area[0].cbr_start =
+                                               win->area[0].cbr_start;
+                       }
                        extend_dev_drv->ops->pan_display(extend_dev_drv,
                                                         extend_win_id);
                }
@@ -1311,10 +1377,7 @@ static void rk_fb_update_driver(struct rk_lcdc_win *win,
                                struct rk_fb_reg_win_data *reg_win_data)
 {
        int i = 0;
-/*
-       struct rk_lcdc_win *win;
-       win = dev_drv->win[reg_win_data->win_id];
-*/
+
        win->area_num = reg_win_data->area_num;
        win->format = reg_win_data->data_format;
        win->id = reg_win_data->win_id;
@@ -1328,22 +1391,16 @@ static void rk_fb_update_driver(struct rk_lcdc_win *win,
                win->z_order = reg_win_data->z_order;
                win->area[0].uv_vir_stride =
                    reg_win_data->reg_area_data[0].uv_vir_stride;
-
-#if !defined(RK_FB_ROTATE) && defined(CONFIG_THREE_FB_BUFFER)
-               /* TODO Mofidy if HDMI info is change to hwc */
                win->area[0].cbr_start =
                    reg_win_data->reg_area_data[0].cbr_start;
-#endif
                win->area[0].c_offset = reg_win_data->reg_area_data[0].c_offset;
                win->alpha_en = reg_win_data->alpha_en;
                win->alpha_mode = reg_win_data->alpha_mode;
                win->g_alpha_val = reg_win_data->g_alpha_val;
                for (i = 0; i < RK_WIN_MAX_AREA; i++) {
                        if (reg_win_data->reg_area_data[i].smem_start > 0) {
-#if !defined(RK_FB_ROTATE) && defined(CONFIG_THREE_FB_BUFFER)
                                win->area[i].smem_start =
                                    reg_win_data->reg_area_data[i].smem_start;
-#endif
                                win->area[i].xpos =
                                    reg_win_data->reg_area_data[i].xpos;
                                win->area[i].ypos =
@@ -1378,6 +1435,189 @@ static void rk_fb_update_driver(struct rk_lcdc_win *win,
 
 }
 
+static int rk_fb_update_hdmi_win(struct rk_lcdc_win *ext_win,
+                                 struct rk_lcdc_win *win)
+{
+        struct rk_fb *rk_fb = platform_get_drvdata(fb_pdev);
+        struct rk_lcdc_driver *dev_drv = rk_get_prmry_lcdc_drv();
+        struct rk_lcdc_driver *ext_dev_drv = rk_get_extend_lcdc_drv();
+        struct rk_screen *screen;
+        struct rk_screen *ext_screen;
+        int hdmi_xsize;
+        int hdmi_ysize;
+        int pixel_width, vir_width_bit, y_stride;
+        struct rk_lcdc_win *last_win;
+        bool is_yuv = false;
+        static u8 fb_index = 0;
+
+        if ((rk_fb->disp_mode != DUAL) ||
+                (hdmi_get_hotplug() != HDMI_HPD_ACTIVED) ||
+                (!hdmi_switch_complete)) {
+                printk(KERN_INFO "%s: hdmi is disconnect!\n", __func__);
+                return -1;
+        }
+
+        if (unlikely(!dev_drv) || unlikely(!ext_dev_drv))
+                return -1;
+
+        screen = dev_drv->cur_screen;
+        ext_screen = ext_dev_drv->cur_screen;
+        hdmi_xsize = ext_screen->xsize;
+        hdmi_ysize = ext_screen->ysize;
+
+        ext_win->state = win->state;
+        ext_win->id = win->id;
+        if (win->area[0].smem_start == 0)
+                ext_win->state = 0;
+        if (ext_win->state == 0)
+                return 0;
+
+       ext_win->area_num = win->area_num;
+       ext_win->format = win->format;
+       ext_win->z_order = win->z_order;
+        ext_win->alpha_en = win->alpha_en;
+        ext_win->alpha_mode = win->alpha_mode;
+       ext_win->g_alpha_val = win->g_alpha_val;
+
+        /* win0 and win1 have only one area and support scale
+         * but win2 and win3 don't support scale
+         * so hdmi only use win0 or win1
+         */
+        ext_win->area[0].state = win->area[0].state;
+
+        switch (ext_win->format) {
+        case YUV422:
+        case YUV420:
+        case YUV444:
+        case YUV422_A:
+        case YUV420_A:
+        case YUV444_A:
+                is_yuv = true;
+                break;
+        default:
+                is_yuv = false;
+                break;
+        }
+
+        if (ext_dev_drv->rotate_mode == ROTATE_90 ||
+                        ext_dev_drv->rotate_mode == ROTATE_270) {
+                if (ext_win->id == 0) {
+                        ext_win->area[0].smem_start =
+                               rk_fb->fb[rk_fb->num_fb >> 1]->fix.smem_start;
+                        ext_win->area[0].y_offset = (get_fb_size() >> 1) * fb_index;
+                        if ((++fb_index) > 1)
+                                fb_index = 0;
+                } else {
+                        ext_win->area[0].y_offset = 0;
+                        last_win = ext_dev_drv->win[ext_win->id - 1];
+                        if (last_win->area[0].cbr_start)
+                                ext_win->area[0].smem_start =
+                                        last_win->area[0].cbr_start +
+                                        last_win->area[0].c_offset +
+                                        last_win->area[0].xvir * last_win->area[0].yvir;
+                        else
+                                ext_win->area[0].smem_start =
+                                        last_win->area[0].smem_start +
+                                        last_win->area[0].y_offset +
+                                        last_win->area[0].xvir * last_win->area[0].yvir;
+                }
+
+                ext_win->area[0].xact = win->area[0].yact;
+                ext_win->area[0].yact = win->area[0].xact;
+                ext_win->area[0].xvir = win->area[0].yact;
+                ext_win->area[0].yvir = win->area[0].xact;
+                pixel_width = rk_fb_pixel_width(ext_win->format);
+                vir_width_bit = pixel_width * ext_win->area[0].xvir;
+                y_stride = ALIGN_N_TIMES(vir_width_bit, 32) / 8;
+                ext_win->area[0].y_vir_stride = y_stride >> 2;
+       } else {
+               ext_win->area[0].smem_start = win->area[0].smem_start;
+                ext_win->area[0].y_offset = win->area[0].y_offset;
+                ext_win->area[0].xact = win->area[0].xact;
+                ext_win->area[0].yact = win->area[0].yact;
+                ext_win->area[0].xvir = win->area[0].xvir;
+                ext_win->area[0].yvir = win->area[0].yvir;
+                ext_win->area[0].y_vir_stride = win->area[0].y_vir_stride;
+        }
+
+        if (win->area[0].xpos != 0 || win->area[0].ypos != 0) {
+                if (ext_dev_drv->rotate_mode == ROTATE_270) {
+                        int xbom_pos = 0, ybom_pos = 0;
+                        int xtop_pos = 0, ytop_pos = 0;
+
+                        ext_win->area[0].xsize = hdmi_xsize *
+                                                win->area[0].ysize /
+                                                screen->mode.yres;
+                        ext_win->area[0].ysize = hdmi_ysize *
+                                                win->area[0].xsize /
+                                                screen->mode.xres;
+                        xbom_pos = hdmi_xsize * win->area[0].ypos / screen->mode.yres;
+                        ybom_pos = hdmi_ysize * win->area[0].xpos / screen->mode.xres;
+                        xtop_pos = hdmi_xsize - ext_win->area[0].xsize - xbom_pos;
+                        ytop_pos = hdmi_ysize - ext_win->area[0].ysize - ybom_pos;
+                        ext_win->area[0].xpos =
+                                ((ext_screen->mode.xres - hdmi_xsize) >> 1) + xtop_pos;
+                        ext_win->area[0].ypos =
+                                ((ext_screen->mode.yres - hdmi_ysize) >> 1) + ytop_pos;
+                } else if (ext_dev_drv->rotate_mode == ROTATE_90) {
+                        ext_win->area[0].xsize = hdmi_xsize *
+                                                win->area[0].ysize /
+                                                screen->mode.yres;
+                        ext_win->area[0].ysize = hdmi_ysize *
+                                                win->area[0].xsize /
+                                                screen->mode.xres;
+                        ext_win->area[0].xpos =
+                                ((ext_screen->mode.xres - hdmi_xsize) >> 1) +
+                                hdmi_xsize * win->area[0].ypos / screen->mode.yres;
+                        ext_win->area[0].ypos =
+                                ((ext_screen->mode.yres - hdmi_ysize) >> 1) +
+                                hdmi_ysize * win->area[0].xpos / screen->mode.xres;
+                } else {
+                        ext_win->area[0].xsize = hdmi_xsize *
+                                                win->area[0].xsize /
+                                                screen->mode.xres;
+                        ext_win->area[0].ysize = hdmi_ysize *
+                                                win->area[0].ysize /
+                                                screen->mode.yres;
+                        ext_win->area[0].xpos =
+                                ((ext_screen->mode.xres - hdmi_xsize) >> 1) +
+                                hdmi_xsize * win->area[0].xpos / screen->mode.xres;
+                        ext_win->area[0].ypos =
+                                ((ext_screen->mode.yres - hdmi_ysize) >> 1) +
+                                hdmi_ysize * win->area[0].ypos / screen->mode.yres;
+                }
+        } else {
+                ext_win->area[0].xsize = hdmi_xsize;
+                ext_win->area[0].ysize = hdmi_ysize;
+                ext_win->area[0].xpos =
+                                (ext_screen->mode.xres - hdmi_xsize) >> 1;
+                ext_win->area[0].ypos =
+                                (ext_screen->mode.yres - hdmi_ysize) >> 1;
+        }
+
+        if (!is_yuv) {
+                ext_win->area[0].uv_vir_stride = 0;
+               ext_win->area[0].cbr_start = 0;
+               ext_win->area[0].c_offset = 0;
+                return 0;
+        }
+
+        if (ext_dev_drv->rotate_mode == ROTATE_90 ||
+                ext_dev_drv->rotate_mode == ROTATE_270) {
+                ext_win->area[0].uv_vir_stride = ext_win->area[0].y_vir_stride;
+                ext_win->area[0].cbr_start = ext_win->area[0].smem_start +
+                                ext_win->area[0].y_offset +
+                                ext_win->area[0].xvir * ext_win->area[0].yvir;
+               ext_win->area[0].c_offset = win->area[0].c_offset;
+        } else {
+               ext_win->area[0].uv_vir_stride = win->area[0].uv_vir_stride;
+               ext_win->area[0].cbr_start = win->area[0].cbr_start;
+               ext_win->area[0].c_offset = win->area[0].c_offset;
+        }
+
+        return 0;
+}
+
 static struct rk_fb_reg_win_data *rk_fb_get_win_data(struct rk_fb_reg_data
                                                     *regs, int win_id)
 {
@@ -1403,7 +1643,6 @@ static void rk_fb_update_reg(struct rk_lcdc_driver *dev_drv,
        struct rk_lcdc_driver *ext_dev_drv;
        struct rk_lcdc_win *ext_win;
        struct rk_fb_reg_win_data *win_data;
-       struct rk_fb_reg_win_data *last_win_data;
        bool wait_for_vsync;
        int count = 100;
        unsigned int dsp_addr[4];
@@ -1444,65 +1683,33 @@ static void rk_fb_update_reg(struct rk_lcdc_driver *dev_drv,
        if ((rk_fb->disp_mode == DUAL)
            && (hdmi_get_hotplug() == HDMI_HPD_ACTIVED)
            && hdmi_switch_complete) {
-               for (i = 0; i < rk_fb->num_lcdc; i++) {
-                       if (rk_fb->lcdc_dev_drv[i]->prop == EXTEND) {
-                               ext_dev_drv = rk_fb->lcdc_dev_drv[i];
-                               break;
-                       }
-               }
-               if (i == rk_fb->num_lcdc) {
-                       printk(KERN_ERR "hdmi lcdc driver not found!\n");
-                       goto ext_win_exit;
-               }
-               /* hdmi just need set win0 only(win0 have only one area)
-                * other win is disable
-                */
-               win = dev_drv->win[0];
-               ext_win = ext_dev_drv->win[0];
-               for (j = 0; j < regs->win_num; j++) {
-                       if (0 == regs->reg_win_data[j].win_id)
-                               break;
-               }
-               if (j < regs->win_num) {
-                       rk_fb_update_driver(ext_win, &regs->reg_win_data[j]);
-                       if (win->area[0].xpos != 0 || win->area[0].ypos != 0) {
-                               ext_win->area[0].xsize =
-                                   (ext_dev_drv->cur_screen->xsize * win->area[0].xsize) /
-                                   dev_drv->cur_screen->mode.xres;
-                               ext_win->area[0].ysize =
-                                   (ext_dev_drv->cur_screen->ysize * win->area[0].ysize) /
-                                   dev_drv->cur_screen->mode.yres;
-                               ext_win->area[0].xpos =
-                                   ((ext_dev_drv->cur_screen->mode.xres - ext_dev_drv->cur_screen->xsize) >> 1) +
-                                   ext_dev_drv->cur_screen->xsize * win->area[0].xpos / dev_drv->cur_screen->mode.xres;
-                               ext_win->area[0].ypos =
-                                   ((ext_dev_drv->cur_screen->mode.yres - ext_dev_drv->cur_screen->ysize) >> 1) +
-                                   ext_dev_drv->cur_screen->ysize * win->area[0].ypos / dev_drv->cur_screen->mode.yres;
-                       } else {
-                               ext_win->area[0].xpos =
-                                   (ext_dev_drv->cur_screen->mode.xres - ext_dev_drv->cur_screen->xsize) >> 1;
-                               ext_win->area[0].ypos =
-                                   (ext_dev_drv->cur_screen->mode.yres - ext_dev_drv->cur_screen->ysize) >> 1;
-                               ext_win->area[0].xsize = ext_dev_drv->cur_screen->xsize;
-                               ext_win->area[0].ysize = ext_dev_drv->cur_screen->ysize;
-                       }
-                       /* hdmi only one win so disable alpha */
-                       ext_win->alpha_en = 0;
-                       ext_win->state = 1;
-               } else {
-                       ext_win->state = 0;
-               }
+                ext_dev_drv = rk_get_extend_lcdc_drv();
+                if (!ext_dev_drv) {
+                        printk(KERN_ERR "hdmi lcdc driver not found!\n");
+                        goto ext_win_exit;
+                }
+
+                /* win0 and win1 have only one area and support scale
+                 * but win2 and win3 don't support scale
+                 * so hdmi only use win0 or win1
+                 */
+               for (i = 0; i < 2; i++) {
+                       win = dev_drv->win[i];
+                       ext_win = ext_dev_drv->win[i];
+                        ext_win->state = win->state;
+                        if (!ext_win->state)
+                                continue;
+
+                        rk_fb_update_hdmi_win(ext_win, win);
+
+                        if (ext_dev_drv->rotate_mode > X_Y_MIRROR)
+                               rk_fb_win_rotate(ext_win, win,
+                                                ext_dev_drv->rotate_mode);
+
+                        ext_dev_drv->ops->set_par(ext_dev_drv, i);
+                       ext_dev_drv->ops->pan_display(ext_dev_drv, i);
+                }
 
-               if (ext_win->area[0].xact < ext_win->area[0].yact) {
-                       ext_win->area[0].xact = win->area[0].yact;
-                       ext_win->area[0].yact = win->area[0].xact;
-                       ext_win->area[0].xvir = win->area[0].yact;
-               }
-#if defined(CONFIG_FB_ROTATE) || !defined(CONFIG_THREE_FB_BUFFER)
-               rk_fb_win_rotate(ext_win, win);
-#endif
-               ext_dev_drv->ops->set_par(ext_dev_drv, 0);
-               ext_dev_drv->ops->pan_display(ext_dev_drv, 0);
                ext_dev_drv->ops->cfg_done(ext_dev_drv);
        }
 ext_win_exit:
@@ -1578,7 +1785,7 @@ static void rk_fb_update_regs_handler(struct kthread_work *work)
        }
 
        if (dev_drv->wait_fs && list_empty(&dev_drv->update_regs_list))
-               wake_up_interruptible_all(&dev_drv->update_regs_wait);
+               wake_up(&dev_drv->update_regs_wait);
 }
 
 static int rk_fb_check_config_var(struct rk_fb_area_par *area_par,
@@ -1937,13 +2144,18 @@ static int rk_fb_set_win_config(struct fb_info *info,
                list_is_empty = list_empty(&dev_drv->update_regs_list) &&
                                        list_empty(&saved_list);
                mutex_unlock(&dev_drv->update_regs_list_lock);
-               if (!list_is_empty)
-                       ret = wait_event_interruptible(dev_drv->update_regs_wait,
-                               list_empty(&dev_drv->update_regs_list) && list_empty(&saved_list));
-               if (!ret) {
+               if (!list_is_empty) {
+                       ret = wait_event_timeout(dev_drv->update_regs_wait,
+                               list_empty(&dev_drv->update_regs_list) && list_empty(&saved_list),
+                               msecs_to_jiffies(60));
+                       if (ret > 0)
+                               rk_fb_update_reg(dev_drv, regs);
+                       else
+                               printk("%s: wait update_regs_wait timeout\n", __func__);
+               } else if (ret == 0) {
                        rk_fb_update_reg(dev_drv, regs);
-                       kfree(regs);
                }
+               kfree(regs);
        }
 
 err:
@@ -1959,23 +2171,25 @@ static int cfgdone_lasttime;
 
 int rk_get_real_fps(int before)
 {
-       if (before > 100)
-               before = 100;
-       if (before < 0)
-               before = 0;
-
        struct timespec now;
-       getnstimeofday(&now);
-       int dist_curr =
-           (now.tv_sec * 1000000 + now.tv_nsec / 1000) - cfgdone_lasttime;
+       int dist_curr;
        int dist_total = 0;
        int dist_count = 0;
        int dist_first = 0;
 
        int index = cfgdone_index;
        int i = 0, fps = 0;
-       int total = dist_curr;
+       int total;
 
+       if (before > 100)
+               before = 100;
+       if (before < 0)
+               before = 0;
+
+       getnstimeofday(&now);
+       dist_curr = (now.tv_sec * 1000000 + now.tv_nsec / 1000) -
+                       cfgdone_lasttime;
+       total = dist_curr;
        /*
           printk("fps: ");
         */
@@ -2888,6 +3102,9 @@ int rk_fb_switch_screen(struct rk_screen *screen, int enable, int lcdc_id)
                        memcpy(dev_drv->cur_screen, screen, sizeof(struct rk_screen));
        }
 
+       dev_drv->cur_screen->x_mirror = dev_drv->rotate_mode & X_MIRROR;
+       dev_drv->cur_screen->y_mirror = dev_drv->rotate_mode & Y_MIRROR;
+
        win_id = dev_drv->ops->fb_get_win_id(dev_drv, info->fix.id);
 
        if (!enable && !dev_drv->screen1) {     /* only double lcdc device need to close */
@@ -3084,12 +3301,11 @@ static int rk_fb_alloc_buffer(struct fb_info *fbi, int fb_id)
        int win_id;
        int ret = 0;
        unsigned long fb_mem_size;
-/*
-#if defined(CONFIG_FB_ROTATE) || !defined(CONFIG_THREE_FB_BUFFER)
-       struct resource *res;
-       struct resource *mem;
+#if !defined(CONFIG_ION_ROCKCHIP)
+       dma_addr_t fb_mem_phys;
+       void *fb_mem_virt;
 #endif
-*/
+
        win_id = dev_drv->ops->fb_get_win_id(dev_drv, fbi->fix.id);
        if (win_id < 0)
                return -ENODEV;
@@ -3102,8 +3318,6 @@ static int rk_fb_alloc_buffer(struct fb_info *fbi, int fb_id)
                if (rk_fb_alloc_buffer_by_ion(fbi, win, fb_mem_size) < 0)
                        return -ENOMEM;
 #else
-               dma_addr_t fb_mem_phys;
-               void *fb_mem_virt;
                fb_mem_virt = dma_alloc_writecombine(fbi->dev, fb_mem_size,
                                                     &fb_mem_phys, GFP_KERNEL);
                if (!fb_mem_virt) {
@@ -3120,45 +3334,29 @@ static int rk_fb_alloc_buffer(struct fb_info *fbi, int fb_id)
                       fbi->fix.smem_start, fbi->screen_base,
                       fbi->fix.smem_len);
        } else {
-#if defined(CONFIG_FB_ROTATE) || !defined(CONFIG_THREE_FB_BUFFER)
-               /*
-                  res = platform_get_resource_byname(fb_pdev,
-                  IORESOURCE_MEM, "fb2 buf");
-                  if (res == NULL) {
-                  dev_err(&fb_pdev->dev, "failed to get win0 memory\n");
-                  ret = -ENOENT;
-                  }
-                  fbi->fix.smem_start = res->start;
-                  fbi->fix.smem_len = res->end - res->start + 1;
-                  mem = request_mem_region(res->start, resource_size(res),
-                  fb_pdev->name);
-                  fbi->screen_base = ioremap(res->start, fbi->fix.smem_len);
-                  memset(fbi->screen_base, 0, fbi->fix.smem_len);
-                */
-               fb_mem_size = get_fb_size();
+               if (dev_drv->rotate_mode > X_Y_MIRROR) {
+                       fb_mem_size = get_fb_size();
 #if defined(CONFIG_ION_ROCKCHIP)
-               if (rk_fb_alloc_buffer_by_ion(fbi, win, fb_mem_size) < 0)
-                       return -ENOMEM;
+                       if (rk_fb_alloc_buffer_by_ion(fbi, win, fb_mem_size) < 0)
+                               return -ENOMEM;
 #else
-               dma_addr_t fb_mem_phys;
-               void *fb_mem_virt;
-               fb_mem_virt = dma_alloc_writecombine(fbi->dev, fb_mem_size,
-                                                    &fb_mem_phys, GFP_KERNEL);
-               if (!fb_mem_virt) {
-                       pr_err("%s: Failed to allocate framebuffer\n",
-                              __func__);
-                       return -ENOMEM;
-               }
-               fbi->fix.smem_len = fb_mem_size;
-               fbi->fix.smem_start = fb_mem_phys;
-               fbi->screen_base = fb_mem_virt;
+                       fb_mem_virt = dma_alloc_writecombine(fbi->dev,
+                                       fb_mem_size, &fb_mem_phys, GFP_KERNEL);
+                       if (!fb_mem_virt) {
+                               pr_err("%s: Failed to allocate framebuffer\n",
+                                       __func__);
+                               return -ENOMEM;
+                       }
+                       fbi->fix.smem_len = fb_mem_size;
+                       fbi->fix.smem_start = fb_mem_phys;
+                       fbi->screen_base = fb_mem_virt;
 #endif
+               } else {
+                       fbi->fix.smem_start = rk_fb->fb[0]->fix.smem_start;
+                       fbi->fix.smem_len = rk_fb->fb[0]->fix.smem_len;
+                       fbi->screen_base = rk_fb->fb[0]->screen_base;
+               }
 
-#else  /* three buffer no need to copy */
-               fbi->fix.smem_start = rk_fb->fb[0]->fix.smem_start;
-               fbi->fix.smem_len = rk_fb->fb[0]->fix.smem_len;
-               fbi->screen_base = rk_fb->fb[0]->screen_base;
-#endif
                printk(KERN_INFO "fb%d:phy:%lx>>vir:%p>>len:0x%x\n", fb_id,
                       fbi->fix.smem_start, fbi->screen_base,
                       fbi->fix.smem_len);
@@ -3174,6 +3372,7 @@ static int rk_fb_alloc_buffer(struct fb_info *fbi, int fb_id)
        return ret;
 }
 
+#if 0
 static int rk_release_fb_buffer(struct fb_info *fbi)
 {
        /* buffer for fb1 and fb3 are alloc by android */
@@ -3184,6 +3383,7 @@ static int rk_release_fb_buffer(struct fb_info *fbi)
        return 0;
 
 }
+#endif
 
 static int init_lcdc_win(struct rk_lcdc_driver *dev_drv,
                         struct rk_lcdc_win *def_win)
@@ -3226,6 +3426,10 @@ static int init_lcdc_device_driver(struct rk_fb *rk_fb,
        screen->overscan.top = 100;
        screen->overscan.right = 100;
        screen->overscan.bottom = 100;
+
+       screen->x_mirror = dev_drv->rotate_mode & X_MIRROR;
+       screen->y_mirror = dev_drv->rotate_mode & Y_MIRROR;
+
        dev_drv->screen0 = screen;
        dev_drv->cur_screen = screen;
        /* devie use one lcdc + rk61x scaler for dual display */