video: rockchip: rk322x: use htotal to adjust fps
authorMark Yao <mark.yao@rock-chips.com>
Thu, 3 Nov 2016 07:52:20 +0000 (15:52 +0800)
committerMark Yao <mark.yao@rock-chips.com>
Wed, 9 Nov 2016 03:31:05 +0000 (11:31 +0800)
Use dclk to adjust fps may effect many things, some display
controller also need to know dclk update, such as mipi.
So if we change fps by dclk, we need update mipi or other
display controller, it waste time and screen would flash

From the fps formula: fps = dclk / htotal * vtotal

Instead change dclk, we also can modify fps by htotal or vtotal.

On testing, vtotal would effect display, use htotal is more safe.

Change-Id: I67d58a815eae150b4e6390b6608557b87d43a27b
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
drivers/video/rockchip/lcdc/rk322x_lcdc.c
drivers/video/rockchip/rkfb_sysfs.c

index 26c49a36bf82800d75cc201f4a6f407157ab32f3..7a9b67d7a1f766b8894184585779e4b59d9fd76d 100644 (file)
@@ -4207,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;
 }
index 965e2df1a67073e963bb074a4bb553d9e1402d84..4393d01c264489360b8993fa24bb5339cdc3e0b1 100644 (file)
@@ -743,17 +743,22 @@ static ssize_t set_fps(struct device *dev, struct device_attribute *attr,
        struct fb_info *fbi = dev_get_drvdata(dev);
        struct rk_fb_par *fb_par = (struct rk_fb_par *)fbi->par;
        struct rk_lcdc_driver *dev_drv = fb_par->lcdc_drv;
-       u32 fps;
+       struct rk_screen *screen = dev_drv->cur_screen;
+       u32 fps, origin_fps;
        int ret;
 
        ret = kstrtou32(buf, 0, &fps);
        if (ret)
                return ret;
 
-       if (fps == 0 || fps > 60) {
-               dev_info(dev, "unsupport fps value,pelase set 1~60\n");
-               return count;
-       }
+       origin_fps = rk_fb_calc_fps(screen, dev_drv->pixclock);
+
+       /*
+        * use too low or too high fps would make screen abnormal,
+        * and maybe can't recovery, so limit the fps.
+        */
+       if (fps <= 40 || fps > origin_fps)
+               fps = origin_fps;
 
        if (dev_drv->ops->fps_mgr)
                ret = dev_drv->ops->fps_mgr(dev_drv, fps, 1);