From 28fc1091f469bfb93623ff289fcc915373c160ae Mon Sep 17 00:00:00 2001 From: Mark Yao Date: Thu, 3 Nov 2016 15:52:20 +0800 Subject: [PATCH] video: rockchip: rk322x: use htotal to adjust fps 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 --- drivers/video/rockchip/lcdc/rk322x_lcdc.c | 90 +++++++++++++++-------- drivers/video/rockchip/rkfb_sysfs.c | 15 ++-- 2 files changed, 69 insertions(+), 36 deletions(-) diff --git a/drivers/video/rockchip/lcdc/rk322x_lcdc.c b/drivers/video/rockchip/lcdc/rk322x_lcdc.c index 26c49a36bf82..7a9b67d7a1f7 100644 --- a/drivers/video/rockchip/lcdc/rk322x_lcdc.c +++ b/drivers/video/rockchip/lcdc/rk322x_lcdc.c @@ -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; } diff --git a/drivers/video/rockchip/rkfb_sysfs.c b/drivers/video/rockchip/rkfb_sysfs.c index 965e2df1a670..4393d01c2644 100644 --- a/drivers/video/rockchip/rkfb_sysfs.c +++ b/drivers/video/rockchip/rkfb_sysfs.c @@ -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); -- 2.34.1