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;
}
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);