ddrfreq: rockchip: read the parameters of auto-freq from dts
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rockchip / ddr_freq.c
index b9c3b19e43ff61c590215dbd110756a93f931901..470587b22efda502e63c9e0172d9f3197277401f 100644 (file)
@@ -1,5 +1,7 @@
 #define pr_fmt(fmt) "ddrfreq: " fmt
+#define DEBUG
 #include <linux/clk.h>
+#include <linux/fb.h>
 #include <linux/cpu.h>
 #include <linux/cpufreq.h>
 #include <linux/delay.h>
 #include <linux/uaccess.h>
 #include <linux/sched/rt.h>
 #include <linux/of.h>
+#include <linux/fb.h>
+#include <linux/input.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 #include <linux/vmalloc.h>
+#include <linux/rockchip/common.h>
+#include <linux/rockchip/cpu_axi.h>
 #include <linux/rockchip/dvfs.h>
 #include <dt-bindings/clock/ddr.h>
+#include <dt-bindings/clock/rk_system_status.h>
+#include <asm/io.h>
+#include <linux/rockchip/grf.h>
+#include <linux/rockchip/iomap.h>
+#include <linux/clk-private.h>
+#include <linux/rockchip/cpu.h>
+#include "../../../drivers/clk/rockchip/clk-pd.h"
 
-#include "common.h"
+static DECLARE_COMPLETION(ddrfreq_completion);
+static DEFINE_MUTEX(ddrfreq_mutex);
+
+#define VOP_REQ_BLOCK
+#ifdef VOP_REQ_BLOCK
+static DECLARE_COMPLETION(vop_req_completion);
+#endif
+
+static struct dvfs_node *clk_cpu_dvfs_node = NULL;
+static int ddr_boost = 0;
+static int print=0;
+static int watch=0;
+static int high_load = 70;
+static int low_load = 60;
+static int auto_freq_interval_ms = 20;
+static int down_rate_delay_ms = 500;
+static unsigned long *auto_freq_table = NULL;
+static int cur_freq_index;
+static int auto_freq_table_size;
+static unsigned long vop_bandwidth_update_jiffies = 0, vop_bandwidth = 0;
+static int vop_bandwidth_update_flag = 0;
+static struct ddr_bw_info ddr_bw_ch0 = {0}, ddr_bw_ch1 = {0};
+static struct cpufreq_frequency_table *bd_freq_table;
 
 enum {
        DEBUG_DDR = 1U << 0,
@@ -27,24 +62,51 @@ enum {
        DEBUG_SUSPEND = 1U << 2,
        DEBUG_VERBOSE = 1U << 3,
 };
-static int debug_mask = DEBUG_DDR;
+static int debug_mask;
+
 module_param(debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
-#define dprintk(mask, fmt, ...) do { if (mask & debug_mask) pr_info(fmt, ##__VA_ARGS__); } while (0)
+#define dprintk(mask, fmt, ...) do { if (mask & debug_mask) pr_debug(fmt, ##__VA_ARGS__); } while (0)
 
 #define MHZ    (1000*1000)
 #define KHZ    1000
 
+struct video_info {
+       int width;
+       int height;
+       int ishevc;
+       int videoFramerate;
+       int streamBitrate;
+
+       struct list_head node;
+};
+struct vop_info {
+       int state;
+       int zone_num;
+       int reserve;
+       int reserve2;
+};
+
+struct bpvopinfo {
+       struct vop_info vopinfo[4];
+       int bp_size;
+       int bp_vop_size;
+};
+
 struct ddr {
-#ifdef CONFIG_HAS_EARLYSUSPEND
-       struct early_suspend early_suspend;
-#endif
        struct dvfs_node *clk_dvfs_node;
+       struct list_head video_info_list;
        unsigned long normal_rate;
-       unsigned long video_rate;
+       unsigned long video_1080p_rate;
+       unsigned long video_4k_rate;
+       unsigned long performance_rate;
        unsigned long dualview_rate;
+       unsigned long hdmi_rate;
        unsigned long idle_rate;
        unsigned long suspend_rate;
        unsigned long reboot_rate;
+       unsigned long boost_rate;
+       unsigned long isp_rate;
+       bool auto_freq;
        bool auto_self_refresh;
        char *mode;
        unsigned long sys_status;
@@ -57,58 +119,283 @@ module_param_named(sys_status, ddr.sys_status, ulong, S_IRUGO);
 module_param_named(auto_self_refresh, ddr.auto_self_refresh, bool, S_IRUGO);
 module_param_named(mode, ddr.mode, charp, S_IRUGO);
 
-static noinline void ddrfreq_set_sys_status(int status)
+static unsigned long auto_freq_round(unsigned long freq)
 {
-       ddr.sys_status |= status;
-       wake_up(&ddr.wait);
+       int i;
+
+       if (!auto_freq_table)
+               return -EINVAL;
+
+       for (i = 0; auto_freq_table[i] != 0; i++) {
+               if (auto_freq_table[i] >= freq) {
+                       return auto_freq_table[i];
+               }
+       }
+
+       return auto_freq_table[i-1];
 }
 
-static noinline void ddrfreq_clear_sys_status(int status)
+static unsigned long auto_freq_get_index(unsigned long freq)
 {
-       ddr.sys_status &= ~status;
-       wake_up(&ddr.wait);
+       int i;
+
+       if (!auto_freq_table)
+               return 0;
+
+       for (i = 0; auto_freq_table[i] != 0; i++) {
+               if (auto_freq_table[i] >= freq) {
+                       return i;
+               }
+       }
+       return i-1;
+}
+
+static unsigned int auto_freq_update_index(unsigned long freq)
+{
+       cur_freq_index = auto_freq_get_index(freq);
+
+       return cur_freq_index;
 }
-int ddr_set_rate(uint32_t nMHz);
 
-static void ddrfreq_mode(bool auto_self_refresh, unsigned long *target_rate, char *name)
+
+static unsigned long auto_freq_get_next_step(void)
 {
+       if (cur_freq_index < auto_freq_table_size-1) {
+                       return auto_freq_table[cur_freq_index+1];
+       }
+
+       return auto_freq_table[cur_freq_index];
+}
+
+static void ddrfreq_mode(bool auto_self_refresh, unsigned long target_rate, char *name)
+{
+       unsigned int min_rate, max_rate;
+       int freq_limit_en;
+
        ddr.mode = name;
        if (auto_self_refresh != ddr.auto_self_refresh) {
                ddr_set_auto_self_refresh(auto_self_refresh);
                ddr.auto_self_refresh = auto_self_refresh;
                dprintk(DEBUG_DDR, "change auto self refresh to %d when %s\n", auto_self_refresh, name);
        }
-       if (*target_rate != dvfs_clk_get_rate(ddr.clk_dvfs_node)) {
-               if (dvfs_clk_set_rate(ddr.clk_dvfs_node, *target_rate) == 0) {
-                       *target_rate = dvfs_clk_get_rate(ddr.clk_dvfs_node);
-                       dprintk(DEBUG_DDR, "change freq to %lu MHz when %s\n", *target_rate / MHZ, name);
+
+       if (target_rate != dvfs_clk_get_last_set_rate(ddr.clk_dvfs_node)) {
+               if (clk_cpu_dvfs_node) {
+                       freq_limit_en = dvfs_clk_get_limit(clk_cpu_dvfs_node,
+                                                          &min_rate,
+                                                          &max_rate);
+
+                       dvfs_clk_enable_limit(clk_cpu_dvfs_node, 600000000, -1);
+               }
+               if (dvfs_clk_set_rate(ddr.clk_dvfs_node, target_rate) == 0) {
+                       target_rate = dvfs_clk_get_rate(ddr.clk_dvfs_node);
+                       auto_freq_update_index(target_rate);
+                       dprintk(DEBUG_DDR, "change freq to %lu MHz when %s\n", target_rate / MHZ, name);
+               }
+               if (clk_cpu_dvfs_node) {
+                       if (freq_limit_en) {
+                               dvfs_clk_enable_limit(clk_cpu_dvfs_node,
+                                                     min_rate, max_rate);
+                       } else {
+                               dvfs_clk_disable_limit(clk_cpu_dvfs_node);
+                       }
+               }
+       }
+}
+
+unsigned long req_freq_by_vop(unsigned long bandwidth)
+{
+       unsigned int i = 0;
+
+       if (time_after(jiffies, vop_bandwidth_update_jiffies +
+               msecs_to_jiffies(down_rate_delay_ms)))
+               return 0;
+
+       if (bd_freq_table == NULL)
+               return 0;
+       for (i = 0; bd_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+               if (bandwidth >= bd_freq_table[i].index)
+                       return bd_freq_table[i].frequency * 1000;
+       }
+
+       return 0;
+}
+
+static void ddr_auto_freq(void)
+{
+       unsigned long freq, new_freq=0, vop_req_freq=0, total_bw_req_freq=0;
+       u32 ddr_percent, target_load;
+       static unsigned long local_jiffies=0, max_ddr_percent=0;
+
+       if (!local_jiffies)
+               local_jiffies = jiffies;
+       freq = dvfs_clk_get_rate(ddr.clk_dvfs_node);
+
+        ddr_bandwidth_get(&ddr_bw_ch0, &ddr_bw_ch1);
+       ddr_percent = ddr_bw_ch0.ddr_percent;
+
+       if ((watch)||(print)) {
+               if((watch == 2)&& (ddr_bw_ch0.ddr_percent < max_ddr_percent)) {
+                   return;
+               } else if(watch == 2) {
+                   max_ddr_percent = ddr_bw_ch0.ddr_percent;
+               }
+               printk("Unit:MB/s total  use%%    rd    wr  cpum   gpu  peri video  vio0  vio1  vio2\n");
+               printk("%3u(ms): %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u\n",
+                       ddr_bw_ch0.ddr_time,
+                       ddr_bw_ch0.ddr_total,
+                       ddr_bw_ch0.ddr_percent,
+                       ddr_bw_ch0.ddr_rd,
+                       ddr_bw_ch0.ddr_wr,
+                       ddr_bw_ch0.cpum,
+                       ddr_bw_ch0.gpu,
+                       ddr_bw_ch0.peri,
+                       ddr_bw_ch0.video,
+                       ddr_bw_ch0.vio0,
+                       ddr_bw_ch0.vio1,
+                       ddr_bw_ch0.vio2);
+
+               if (watch)
+                       return;
+       }
+
+       if (ddr_boost) {
+               ddr_boost = 0;
+               new_freq = max(ddr.boost_rate, new_freq);
+       }
+
+       if(ddr_percent > high_load){
+               total_bw_req_freq = auto_freq_get_next_step();
+       } else if (ddr_percent < low_load){
+               target_load = (low_load+high_load)/2;
+               total_bw_req_freq = ddr_percent*(freq/target_load);
+       }
+       new_freq = max(total_bw_req_freq, new_freq);
+
+       vop_req_freq = req_freq_by_vop(vop_bandwidth);
+       new_freq = max(vop_req_freq, new_freq);
+       if (new_freq == 0)
+               return;
+
+       new_freq = auto_freq_round(new_freq);
+
+       if (new_freq < freq) {
+               if (time_after(jiffies, local_jiffies+down_rate_delay_ms/10)) {
+                       local_jiffies = jiffies;
+                       ddrfreq_mode(false, new_freq, "auto down rate");
                }
+       } else if(new_freq > freq){
+               local_jiffies = jiffies;
+               ddrfreq_mode(false, new_freq, "auto up rate");
        }
 }
 
-static noinline void ddrfreq_work(unsigned long sys_status)
+static noinline long ddrfreq_work(unsigned long sys_status)
 {
-       static struct clk *cpu = NULL;
-       static struct clk *gpu = NULL;
+       long timeout = MAX_SCHEDULE_TIMEOUT;
+       unsigned long target_rate = 0;
        unsigned long s = sys_status;
+       bool auto_self_refresh = false;
+       char *mode = NULL;
 
-       if (!cpu)
-               cpu = clk_get(NULL, "cpu");
-       if (!gpu)
-               gpu = clk_get(NULL, "gpu");
-       
        dprintk(DEBUG_VERBOSE, "sys_status %02lx\n", sys_status);
-       
+
+       if (ddr.reboot_rate && (s & SYS_STATUS_REBOOT)) {
+               ddrfreq_mode(false, ddr.reboot_rate, "shutdown/reboot");
+
+               return timeout;
+       }
+
+       if (ddr.suspend_rate && (s & SYS_STATUS_SUSPEND)) {
+               if (ddr.suspend_rate > target_rate) {
+                       target_rate = ddr.suspend_rate;
+                       auto_self_refresh = true;
+                       mode = "suspend";
+               }
+       }
+
+       if (ddr.performance_rate && (s & SYS_STATUS_PERFORMANCE)) {
+               if (ddr.performance_rate > target_rate) {
+                       target_rate = ddr.performance_rate;
+                       auto_self_refresh = false;
+                       mode = "performance";
+               }
+       }
+
+        if (ddr.dualview_rate &&
+               (s & SYS_STATUS_LCDC0) && (s & SYS_STATUS_LCDC1)) {
+                if (ddr.dualview_rate > target_rate) {
+                        target_rate = ddr.dualview_rate;
+                        auto_self_refresh = false;
+                        mode = "dual-view";
+                }
+        }
+
+        if (ddr.hdmi_rate &&
+               (s & SYS_STATUS_HDMI)) {
+                if (ddr.hdmi_rate > target_rate) {
+                        target_rate = ddr.hdmi_rate;
+                        auto_self_refresh = false;
+                        mode = "hdmi";
+                }
+        }
+
+       if (ddr.video_4k_rate && (s & SYS_STATUS_VIDEO_4K) && !(s & SYS_STATUS_SUSPEND)) {
+               if (ddr.video_4k_rate > target_rate) {
+                       target_rate = ddr.video_4k_rate;
+                       auto_self_refresh = false;
+                       mode = "video_4k";
+               }
+       }
+
+       if (ddr.video_1080p_rate && (s & SYS_STATUS_VIDEO_1080P)) {
+               if (ddr.video_1080p_rate > target_rate) {
+                       target_rate = ddr.video_1080p_rate;
+                       auto_self_refresh = false;
+                       mode = "video_1080p";
+               }
+       }
+
+       if (ddr.isp_rate && (s & SYS_STATUS_ISP)) {
+               if (ddr.isp_rate > target_rate) {
+                       target_rate = ddr.isp_rate;
+                       auto_self_refresh = false;
+                       mode = "isp";
+               }
+       }
+
+       if (target_rate > 0) {
+               ddrfreq_mode(auto_self_refresh, target_rate, mode);
+       } else {
+               if (ddr.auto_freq) {
+                       ddr_auto_freq();
+                       timeout = auto_freq_interval_ms/10;
+               }
+               else {
+                       ddrfreq_mode(false, ddr.normal_rate, "normal");
+               }
+       }
+
+       return timeout;
+#if 0
+
        if (ddr.reboot_rate && (s & SYS_STATUS_REBOOT)) {
                ddrfreq_mode(false, &ddr.reboot_rate, "shutdown/reboot");
+               rockchip_cpufreq_reboot_limit_freq();
+               reboot_config_done = 1;
        } else if (ddr.suspend_rate && (s & SYS_STATUS_SUSPEND)) {
                ddrfreq_mode(true, &ddr.suspend_rate, "suspend");
        } else if (ddr.dualview_rate && 
                (s & SYS_STATUS_LCDC0) && (s & SYS_STATUS_LCDC1)) {
                ddrfreq_mode(false, &ddr.dualview_rate, "dual-view");
-       } else if (ddr.video_rate && 
-               ((s & SYS_STATUS_VIDEO_720P)||(s & SYS_STATUS_VIDEO_1080P))) {
-               ddrfreq_mode(false, &ddr.video_rate, "video");
+       } else if (ddr.video_1080p_rate && (s & SYS_STATUS_VIDEO_1080P)) {
+               ddrfreq_mode(false, &ddr.video_1080p_rate, "video_1080p");
+       } else if (ddr.video_4k_rate && (s & SYS_STATUS_VIDEO_4K)) {
+               ddrfreq_mode(false, &ddr.video_4k_rate, "video_4k");
+       } else if (ddr.performance_rate && (s & SYS_STATUS_PERFORMANCE)) {
+               ddrfreq_mode(false, &ddr.performance_rate, "performance");
+       }  else if (ddr.isp_rate && (s & SYS_STATUS_ISP)) {
+               ddrfreq_mode(false, &ddr.isp_rate, "isp");
        } else if (ddr.idle_rate
                && !(s & SYS_STATUS_GPU)
                && !(s & SYS_STATUS_RGA)
@@ -119,53 +406,135 @@ static noinline void ddrfreq_work(unsigned long sys_status)
                ) {
                ddrfreq_mode(false, &ddr.idle_rate, "idle");
        } else {
-               ddrfreq_mode(false, &ddr.normal_rate, "normal");
+               if (ddr.auto_freq) {
+                       ddr_auto_freq();
+                       timeout = auto_freq_interval_ms;
+               }
+               else {
+                       ddrfreq_mode(false, &ddr.normal_rate, "normal");
+               }
        }
+
+
+
+       return timeout;
+#endif
 }
 
 static int ddrfreq_task(void *data)
 {
+       long timeout;
+       unsigned long status=ddr.sys_status, old_status=ddr.sys_status;
+
        set_freezable();
 
        do {
-               unsigned long status = ddr.sys_status;
-               ddrfreq_work(status);
-               wait_event_freezable(ddr.wait, (status != ddr.sys_status) || kthread_should_stop());
+               status = ddr.sys_status;
+               timeout = ddrfreq_work(status);
+               if (old_status != status)
+                       complete(&ddrfreq_completion);
+               if (vop_bandwidth_update_flag) {
+                       vop_bandwidth_update_flag = 0;
+#ifdef VOP_REQ_BLOCK
+                       complete(&vop_req_completion);
+#endif
+               }
+               wait_event_freezable_timeout(ddr.wait, vop_bandwidth_update_flag || (status != ddr.sys_status) || kthread_should_stop(), timeout);
+               old_status = status;
        } while (!kthread_should_stop());
 
        return 0;
 }
 
-#ifdef CONFIG_HAS_EARLYSUSPEND
-static void ddrfreq_early_suspend(struct early_suspend *h)
+void add_video_info(struct video_info *video_info)
 {
-       dprintk(DEBUG_SUSPEND, "early suspend\n");
-       ddrfreq_set_sys_status(SYS_STATUS_SUSPEND);
+       if (video_info)
+               list_add(&video_info->node, &ddr.video_info_list);
 }
 
-static void ddrfreq_late_resume(struct early_suspend *h)
+void del_video_info(struct video_info *video_info)
 {
-       dprintk(DEBUG_SUSPEND, "late resume\n");
-       ddrfreq_clear_sys_status(SYS_STATUS_SUSPEND);
+       if (video_info) {
+               list_del(&video_info->node);
+               kfree(video_info);
+       }
 }
-#endif
 
-static int video_state_release(struct inode *inode, struct file *file)
+void clear_video_info(void)
 {
-       dprintk(DEBUG_VIDEO_STATE, "video_state release\n");
-       ddrfreq_clear_sys_status(SYS_STATUS_VIDEO);
+       struct video_info *video_info, *next;
+
+       list_for_each_entry_safe(video_info, next, &ddr.video_info_list, node) {
+               del_video_info(video_info);
+       }
+}
+
+struct video_info *find_video_info(struct video_info *match_video_info)
+{
+       struct video_info *video_info;
+
+       if (!match_video_info)
+               return NULL;
+
+       list_for_each_entry(video_info, &ddr.video_info_list, node) {
+               if ((video_info->width == match_video_info->width)
+                       && (video_info->height == match_video_info->height)
+                       && (video_info->ishevc== match_video_info->ishevc)
+                       && (video_info->videoFramerate == match_video_info->videoFramerate)
+                       && (video_info->streamBitrate== match_video_info->streamBitrate)) {
+
+                       return video_info;
+               }
+
+       }
+
+       return NULL;
+}
+
+void update_video_info(void)
+{
+       struct video_info *video_info, *max_res_video;
+       int max_res=0, res=0;
+
+       if (list_empty(&ddr.video_info_list)) {
+               rockchip_clear_system_status(SYS_STATUS_VIDEO_1080P|SYS_STATUS_VIDEO_4K);
+               return;
+       }
+
+       list_for_each_entry(video_info, &ddr.video_info_list, node) {
+               res = video_info->width * video_info->height;
+               if (res > max_res) {
+                       max_res = res;
+                       max_res_video = video_info;
+               }
+       }
+
+       if (max_res <= 1920*1080)
+               rockchip_set_system_status(SYS_STATUS_VIDEO_1080P);
+       else
+               rockchip_set_system_status(SYS_STATUS_VIDEO_4K);
+
+       return;
+}
+
+/***format: width=val,height=val,ishevc=val,videoFramerate=val,streamBitrate=val***/
+static long get_video_param(char **str)
+{
+       char *p;
+
+       strsep(str,"=");
+       p=strsep(str,",");
+       if (p)
+               return simple_strtol(p,NULL,10);
+
        return 0;
 }
 
-#define VIDEO_LOW_RESOLUTION       (1080*720)
 static ssize_t video_state_write(struct file *file, const char __user *buffer,
                                 size_t count, loff_t *ppos)
 {
-       char state;
-       char *cookie_pot;
-       char *p;
-       char *buf = vzalloc(count);
-       uint32_t v_width=0,v_height=0,v_sync=0;
+       struct video_info *video_info = NULL;
+       char state, *cookie_pot, *buf = vzalloc(count);
        cookie_pot = buf;
 
        if(!buf)
@@ -181,51 +550,67 @@ static ssize_t video_state_write(struct file *file, const char __user *buffer,
                return -EFAULT;
        }
 
-       dprintk(DEBUG_VIDEO_STATE, "video_state write %s,len %d\n", cookie_pot,count);
+       dprintk(DEBUG_VIDEO_STATE, "%s: %s,len %zu\n", __func__, cookie_pot,count);
 
        state=cookie_pot[0];
        if( (count>=3) && (cookie_pot[2]=='w') )
        {
+               video_info = kzalloc(sizeof(struct video_info), GFP_KERNEL);
+               if (!video_info){
+                       vfree(buf);
+                       return -ENOMEM;
+               }
+               INIT_LIST_HEAD(&video_info->node);
+
                strsep(&cookie_pot,",");
-               strsep(&cookie_pot,"=");
-               p=strsep(&cookie_pot,",");
-               v_width = simple_strtol(p,NULL,10);
-               strsep(&cookie_pot,"=");
-               p=strsep(&cookie_pot,",");
-               v_height= simple_strtol(p,NULL,10);
-               strsep(&cookie_pot,"=");
-               p=strsep(&cookie_pot,",");
-               v_sync= simple_strtol(p,NULL,10);
-               dprintk(DEBUG_VIDEO_STATE, "video_state %c,width=%d,height=%d,sync=%d\n", state,v_width,v_height,v_sync);
-       }
 
+               video_info->width = get_video_param(&cookie_pot);
+               video_info->height = get_video_param(&cookie_pot);
+               video_info->ishevc = get_video_param(&cookie_pot);
+               video_info->videoFramerate = get_video_param(&cookie_pot);
+               video_info->streamBitrate = get_video_param(&cookie_pot);
+
+               dprintk(DEBUG_VIDEO_STATE, "%s: video_state=%c,width=%d,height=%d,ishevc=%d,videoFramerate=%d,streamBitrate=%d\n",
+                       __func__, state,video_info->width,video_info->height,
+                       video_info->ishevc, video_info->videoFramerate,
+                       video_info->streamBitrate);
+
+       }
        switch (state) {
        case '0':
-               ddrfreq_clear_sys_status(SYS_STATUS_VIDEO);
+               del_video_info(find_video_info(video_info));
+               kfree(video_info);
+               update_video_info();
                break;
        case '1':
-               if( (v_width == 0) && (v_height == 0)){
-                       ddrfreq_set_sys_status(SYS_STATUS_VIDEO_1080P);
-               }
-               /*else if(v_sync==1){
-                       if(ddr.video_low_rate && ((v_width*v_height) <= VIDEO_LOW_RESOLUTION) )
-                               ddrfreq_set_sys_status(SYS_STATUS_VIDEO_720P);
-                       else
-                               ddrfreq_set_sys_status(SYS_STATUS_VIDEO_1080P);
-               }*/
-               else{
-                       ddrfreq_clear_sys_status(SYS_STATUS_VIDEO);
-               }
+               add_video_info(video_info);
+               update_video_info();
+               break;
+       case 'p'://performance
+               rockchip_set_system_status(SYS_STATUS_PERFORMANCE);
+               break;
+       case 'n'://normal
+               rockchip_clear_system_status(SYS_STATUS_PERFORMANCE);
                break;
        default:
                vfree(buf);
                return -EINVAL;
 
        }
+
        vfree(buf);
        return count;
 }
 
+static int video_state_release(struct inode *inode, struct file *file)
+{
+       dprintk(DEBUG_VIDEO_STATE, "video_state release\n");
+       clear_video_info();
+       update_video_info();
+       return 0;
+}
+
+
 static const struct file_operations video_state_fops = {
        .owner  = THIS_MODULE,
        .release= video_state_release,
@@ -237,21 +622,136 @@ static struct miscdevice video_state_dev = {
        .name   = "video_state",
        .minor  = MISC_DYNAMIC_MINOR,
 };
-/*
+
+static long ddr_freq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct bpvopinfo *bpvinfo = (struct bpvopinfo *)arg;
+       unsigned long vop_req_freq;
+       int ret = -1;
+
+       vop_bandwidth = bpvinfo->bp_vop_size;
+       vop_bandwidth_update_jiffies = jiffies;
+       vop_req_freq = req_freq_by_vop(vop_bandwidth);
+       if (dvfs_clk_get_rate(ddr.clk_dvfs_node) >= vop_req_freq)
+               ret = 0;
+
+       vop_bandwidth_update_flag = 1;
+       wake_up(&ddr.wait);
+#ifdef VOP_REQ_BLOCK
+       wait_for_completion(&vop_req_completion);
+       if (dvfs_clk_get_rate(ddr.clk_dvfs_node) >= vop_req_freq)
+               ret = 0;
+#endif
+
+       return ret;
+}
+
+
+static const struct file_operations ddr_freq_fops = {
+       .owner  = THIS_MODULE,
+       .unlocked_ioctl = ddr_freq_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ddr_freq_ioctl,
+#endif
+};
+
+static struct miscdevice ddr_freq_dev = {
+       .fops   = &ddr_freq_fops,
+       .name   = "ddr_freq",
+       .mode   = S_IRUGO | S_IWUSR | S_IWUGO,
+       .minor  = MISC_DYNAMIC_MINOR,
+};
+
+#ifdef CONFIG_INPUT
+static void ddr_freq_input_event(struct input_handle *handle, unsigned int type,
+               unsigned int code, int value)
+{
+       if (type == EV_ABS)
+               ddr_boost = 1;
+}
+
+static int ddr_freq_input_connect(struct input_handler *handler,
+               struct input_dev *dev, const struct input_device_id *id)
+{
+       struct input_handle *handle;
+       int error;
+
+       handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+       if (!handle)
+               return -ENOMEM;
+
+       handle->dev = dev;
+       handle->handler = handler;
+       handle->name = "ddr_freq";
+
+       error = input_register_handle(handle);
+       if (error)
+               goto err2;
+
+       error = input_open_device(handle);
+       if (error)
+               goto err1;
+
+       return 0;
+err1:
+       input_unregister_handle(handle);
+err2:
+       kfree(handle);
+       return error;
+}
+
+static void ddr_freq_input_disconnect(struct input_handle *handle)
+{
+       input_close_device(handle);
+       input_unregister_handle(handle);
+       kfree(handle);
+}
+
+static const struct input_device_id ddr_freq_ids[] = {
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+                       INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .evbit = { BIT_MASK(EV_ABS) },
+               .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+                       BIT_MASK(ABS_MT_POSITION_X) |
+                       BIT_MASK(ABS_MT_POSITION_Y) },
+       },
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_KEYBIT |
+                       INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+               .absbit = { [BIT_WORD(ABS_X)] =
+                       BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
+       },
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+               .evbit = { BIT_MASK(EV_KEY) },
+       },
+       { },
+};
+
+static struct input_handler ddr_freq_input_handler = {
+       .event          = ddr_freq_input_event,
+       .connect        = ddr_freq_input_connect,
+       .disconnect     = ddr_freq_input_disconnect,
+       .name           = "ddr_freq",
+       .id_table       = ddr_freq_ids,
+};
+#endif
+#if 0
 static int ddrfreq_clk_event(int status, unsigned long event)
 {
        switch (event) {
-       case PRE_RATE_CHANGE:
+       case RK_CLK_PD_PREPARE:
                ddrfreq_set_sys_status(status);
                break;
-       case POST_RATE_CHANGE:
-       case ABORT_RATE_CHANGE:
+       case RK_CLK_PD_UNPREPARE:
                ddrfreq_clear_sys_status(status);
                break;
        }
        return NOTIFY_OK;
 }
-*/
+
 #define CLK_NOTIFIER(name, status) \
 static int ddrfreq_clk_##name##_event(struct notifier_block *this, unsigned long event, void *ptr) \
 { \
@@ -262,47 +762,111 @@ static struct notifier_block ddrfreq_clk_##name##_notifier = { .notifier_call =
 #define REGISTER_CLK_NOTIFIER(name) \
 do { \
        struct clk *clk = clk_get(NULL, #name); \
-       clk_notifier_register(clk, &ddrfreq_clk_##name##_notifier); \
+       rk_clk_pd_notifier_register(clk, &ddrfreq_clk_##name##_notifier); \
        clk_put(clk); \
 } while (0)
 
 #define UNREGISTER_CLK_NOTIFIER(name) \
 do { \
        struct clk *clk = clk_get(NULL, #name); \
-       clk_notifier_unregister(clk, &ddrfreq_clk_##name##_notifier); \
+       rk_clk_pd_notifier_unregister(clk, &ddrfreq_clk_##name##_notifier); \
        clk_put(clk); \
 } while (0)
-/*
-CLK_NOTIFIER(pd_gpu, GPU);
-CLK_NOTIFIER(pd_rga, RGA);
-CLK_NOTIFIER(pd_cif0, CIF0);
-CLK_NOTIFIER(pd_cif1, CIF1);
-CLK_NOTIFIER(pd_lcdc0, LCDC0);
-CLK_NOTIFIER(pd_lcdc1, LCDC1);
-*/
-static int ddrfreq_reboot_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
+
+CLK_NOTIFIER(pd_isp, ISP)
+CLK_NOTIFIER(pd_vop0, LCDC0)
+CLK_NOTIFIER(pd_vop1, LCDC1)
+#endif
+
+static int ddr_freq_suspend_notifier_call(struct notifier_block *self,
+                               unsigned long action, void *data)
 {
-       u32 timeout = 1000; // 10s
-       ddrfreq_set_sys_status(SYS_STATUS_REBOOT);
-       while (dvfs_clk_get_rate(ddr.clk_dvfs_node) != ddr.reboot_rate && --timeout) {
-               msleep(10);
+       struct fb_event *event = data;
+       int blank_mode = *((int *)event->data);
+
+       if (action == FB_EARLY_EVENT_BLANK) {
+               switch (blank_mode) {
+               case FB_BLANK_UNBLANK:
+                       rockchip_clear_system_status(SYS_STATUS_SUSPEND);
+                       break;
+               default:
+                       break;
+               }
        }
-       if (!timeout) {
-               pr_err("failed to set ddr clk from %luMHz to %luMHz when shutdown/reboot\n", dvfs_clk_get_rate(ddr.clk_dvfs_node) / MHZ, ddr.reboot_rate / MHZ);
+       else if (action == FB_EVENT_BLANK) {
+               switch (blank_mode) {
+               case FB_BLANK_POWERDOWN:
+                       rockchip_set_system_status(SYS_STATUS_SUSPEND);
+                       break;
+               default:
+                       break;
+               }
        }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block ddr_freq_suspend_notifier = {
+               .notifier_call = ddr_freq_suspend_notifier_call,
+};
+
+static int ddrfreq_system_status_notifier_call(struct notifier_block *nb,
+                               unsigned long val, void *data)
+{
+       mutex_lock(&ddrfreq_mutex);
+       ddr.sys_status = val;
+       wake_up(&ddr.wait);
+       wait_for_completion(&ddrfreq_completion);
+       mutex_unlock(&ddrfreq_mutex);
+
        return NOTIFY_OK;
 }
 
-static struct notifier_block ddrfreq_reboot_notifier = {
-       .notifier_call = ddrfreq_reboot_notifier_event,
+static struct notifier_block ddrfreq_system_status_notifier = {
+               .notifier_call = ddrfreq_system_status_notifier_call,
 };
 
+static struct cpufreq_frequency_table
+       *of_get_bd_freq_table(struct device_node *np, const char *propname)
+{
+       struct cpufreq_frequency_table *freq_table = NULL;
+       const struct property *prop;
+       const __be32 *val;
+       int nr, i;
+
+       prop = of_find_property(np, propname, NULL);
+       if (!prop)
+               return NULL;
+       if (!prop->value)
+               return NULL;
+
+       nr = prop->length / sizeof(u32);
+       if (nr % 2) {
+               pr_err("%s: Invalid freq list\n", __func__);
+               return NULL;
+       }
+
+       freq_table = kzalloc(sizeof(*freq_table) * (nr/2 + 1), GFP_KERNEL);
+
+       val = prop->value;
+
+       for (i = 0; i < nr/2; i++) {
+               freq_table[i].index = be32_to_cpup(val++);
+               freq_table[i].frequency = be32_to_cpup(val++);
+       }
+
+       freq_table[i].index = 0;
+       freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+       return freq_table;
+}
+
 int of_init_ddr_freq_table(void)
 {
        struct device_node *clk_ddr_dev_node;
        const struct property *prop;
        const __be32 *val;
-       int nr;
+       int nr, i=0;
        
        clk_ddr_dev_node = of_find_node_by_name(NULL, "clk_ddr");
        if (IS_ERR_OR_NULL(clk_ddr_dev_node)) {
@@ -310,7 +874,25 @@ int of_init_ddr_freq_table(void)
                return PTR_ERR(clk_ddr_dev_node);
        }
 
-       prop = of_find_property(clk_ddr_dev_node, "freq_table", NULL);
+       prop = of_find_property(clk_ddr_dev_node, "auto-freq", NULL);
+       if (prop && prop->value)
+               ddr.auto_freq = be32_to_cpup(prop->value);
+
+       prop = of_find_property(clk_ddr_dev_node, "auto-freq-table", NULL);
+       if (prop && prop->value) {
+               nr = prop->length / sizeof(u32);
+               auto_freq_table = kzalloc((sizeof(u32) *(nr+1)), GFP_KERNEL);
+               val = prop->value;
+               while (nr) {
+                       auto_freq_table[i++] =
+                               dvfs_clk_round_rate(ddr.clk_dvfs_node, 1000 * be32_to_cpup(val++));
+                       nr--;
+               }
+               cur_freq_index = 0;
+               auto_freq_table_size = i;
+       }
+
+       prop = of_find_property(clk_ddr_dev_node, "freq-table", NULL);
        if (!prop)
                return -ENODEV;
        if (!prop->value)
@@ -325,74 +907,118 @@ int of_init_ddr_freq_table(void)
        val = prop->value;
        while (nr) {
                unsigned long status = be32_to_cpup(val++);
-               unsigned long rate = be32_to_cpup(val++) * 1000;
+               unsigned long rate =
+                       dvfs_clk_round_rate(ddr.clk_dvfs_node, be32_to_cpup(val++) * 1000);
 
                if (status & SYS_STATUS_NORMAL)
                        ddr.normal_rate = rate;
                if (status & SYS_STATUS_SUSPEND)
                        ddr.suspend_rate = rate;
-               if ((status & SYS_STATUS_VIDEO_720P)||(status & SYS_STATUS_VIDEO_720P))
-                       ddr.video_rate = rate;
+               if (status & SYS_STATUS_VIDEO_1080P)
+                       ddr.video_1080p_rate = rate;
+               if (status & SYS_STATUS_VIDEO_4K)
+                       ddr.video_4k_rate = rate;
+               if (status & SYS_STATUS_PERFORMANCE)
+                       ddr.performance_rate= rate;
                if ((status & SYS_STATUS_LCDC0)&&(status & SYS_STATUS_LCDC1))
                        ddr.dualview_rate = rate;
+               if (status & SYS_STATUS_HDMI)
+                       ddr.hdmi_rate = rate;
                if (status & SYS_STATUS_IDLE)
                        ddr.idle_rate= rate;
                if (status & SYS_STATUS_REBOOT)
                        ddr.reboot_rate= rate;
+               if (status & SYS_STATUS_BOOST)
+                       ddr.boost_rate= rate;
+               if (status & SYS_STATUS_ISP)
+                       ddr.isp_rate= rate;
 
                nr -= 2;
        }
 
+       bd_freq_table = of_get_bd_freq_table(clk_ddr_dev_node, "bd-freq-table");
+
+       of_property_read_u32_index(clk_ddr_dev_node, "high_load", 0,
+                                  &high_load);
+       of_property_read_u32_index(clk_ddr_dev_node, "low_load", 0, &low_load);
+       of_property_read_u32_index(clk_ddr_dev_node, "auto_freq_interval", 0,
+                                  &auto_freq_interval_ms);
+       of_property_read_u32_index(clk_ddr_dev_node, "down_rate_delay", 0,
+                                  &down_rate_delay_ms);
+
+       return 0;
+}
+
+static int ddrfreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate)
+{
+       unsigned long real_rate;
+
+       real_rate = ddr_change_freq(rate/MHZ);
+       real_rate *= MHZ;
+       if (!real_rate)
+               return -EAGAIN;
+       if (cpu_is_rk312x()) {
+               clk->parent->rate = 2 * real_rate;
+               clk->rate = real_rate;
+       } else {
+               clk->rate = real_rate;
+               clk->parent->rate = real_rate;
+       }
+
        return 0;
 }
 
+#if defined(CONFIG_RK_PM_TESTS)
+static void ddrfreq_tst_init(void);
+#endif
+
 static int ddrfreq_init(void)
 {
+       int ret, i;
        struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
-       int ret;
-       
+
+#if defined(CONFIG_RK_PM_TESTS)
+        ddrfreq_tst_init();
+#endif
+
+       clk_cpu_dvfs_node = clk_get_dvfs_node("clk_core");
+       memset(&ddr, 0x00, sizeof(ddr));
        ddr.clk_dvfs_node = clk_get_dvfs_node("clk_ddr");
        if (!ddr.clk_dvfs_node){
                return -EINVAL;
        }
+       clk_enable_dvfs(ddr.clk_dvfs_node);
+
+       dvfs_clk_register_set_rate_callback(ddr.clk_dvfs_node, ddrfreq_scale_rate_for_dvfs);
        
        init_waitqueue_head(&ddr.wait);
+       INIT_LIST_HEAD(&ddr.video_info_list);
        ddr.mode = "normal";
-
        ddr.normal_rate = dvfs_clk_get_rate(ddr.clk_dvfs_node);
-       ddr.video_rate = ddr.normal_rate;
-       ddr.dualview_rate = 0;
-       ddr.idle_rate = 0;
-       ddr.suspend_rate = ddr.normal_rate;
-       ddr.reboot_rate = ddr.normal_rate;      
+       ddr.sys_status = rockchip_get_system_status();
 
        of_init_ddr_freq_table();
 
-       if (ddr.idle_rate) {
-               //REGISTER_CLK_NOTIFIER(pd_gpu);
-               //REGISTER_CLK_NOTIFIER(pd_rga);
-               //REGISTER_CLK_NOTIFIER(pd_cif0);
-               //REGISTER_CLK_NOTIFIER(pd_cif1);
-       }
+       if (!ddr.reboot_rate)
+               ddr.reboot_rate = ddr.normal_rate;
+
+#ifdef CONFIG_INPUT
+       ret = input_register_handler(&ddr_freq_input_handler);
+       if (ret)
+               ddr.auto_freq = false;
+#endif
 
-       if (ddr.dualview_rate) {
-             //REGISTER_CLK_NOTIFIER(pd_lcdc0);
-             //REGISTER_CLK_NOTIFIER(pd_lcdc1);
-       }       
+       //REGISTER_CLK_NOTIFIER(pd_isp);
+       //REGISTER_CLK_NOTIFIER(pd_vop0);
+       //REGISTER_CLK_NOTIFIER(pd_vop1);
 
        ret = misc_register(&video_state_dev);
+       ret = misc_register(&ddr_freq_dev);
        if (unlikely(ret)) {
                pr_err("failed to register video_state misc device! error %d\n", ret);
                goto err;
        }
 
-#ifdef CONFIG_HAS_EARLYSUSPEND
-       ddr.early_suspend.suspend = ddrfreq_early_suspend;
-       ddr.early_suspend.resume = ddrfreq_late_resume;
-       ddr.early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 50;
-       register_early_suspend(&ddr.early_suspend);
-#endif
-
        ddr.task = kthread_create(ddrfreq_task, NULL, "ddrfreqd");
        if (IS_ERR(ddr.task)) {
                ret = PTR_ERR(ddr.task);
@@ -405,31 +1031,100 @@ static int ddrfreq_init(void)
        kthread_bind(ddr.task, 0);
        wake_up_process(ddr.task);
 
-       register_reboot_notifier(&ddrfreq_reboot_notifier);
+       rockchip_register_system_status_notifier(&ddrfreq_system_status_notifier);
+       fb_register_client(&ddr_freq_suspend_notifier);
 
-       pr_info("verion 1.0 20140228\n");
-       dprintk(DEBUG_DDR, "normal %luMHz video %luMHz dualview %luMHz idle %luMHz suspend %luMHz reboot %luMHz\n",
-               ddr.normal_rate / MHZ, ddr.video_rate / MHZ, ddr.dualview_rate / MHZ, ddr.idle_rate / MHZ, ddr.suspend_rate / MHZ, ddr.reboot_rate / MHZ);
+       pr_info("verion 1.2 20140526\n");
+       pr_info("normal %luMHz video_1080p %luMHz video_4k %luMHz dualview %luMHz idle %luMHz suspend %luMHz reboot %luMHz\n",
+               ddr.normal_rate / MHZ,
+               ddr.video_1080p_rate / MHZ,
+               ddr.video_4k_rate / MHZ,
+               ddr.dualview_rate / MHZ,
+               ddr.idle_rate / MHZ,
+               ddr.suspend_rate / MHZ,
+               ddr.reboot_rate / MHZ);
 
+       pr_info("auto-freq=%d\n", ddr.auto_freq);
+       if (auto_freq_table) {
+               for (i = 0; i < auto_freq_table_size; i++) {
+                       pr_info("auto-freq-table[%d] %luMHz\n", i, auto_freq_table[i] / MHZ);
+               }
+       } else {
+               pr_info("auto-freq-table epmty!\n");
+       }
        return 0;
 
 err1:
-#ifdef CONFIG_HAS_EARLYSUSPEND
-       unregister_early_suspend(&ddr.early_suspend);
-#endif
        misc_deregister(&video_state_dev);
 err:
-       if (ddr.idle_rate) {
-               //UNREGISTER_CLK_NOTIFIER(pd_gpu);
-               //UNREGISTER_CLK_NOTIFIER(pd_rga);
-               //UNREGISTER_CLK_NOTIFIER(pd_cif0);
-               //UNREGISTER_CLK_NOTIFIER(pd_cif1);
-       }
-       if (ddr.dualview_rate) {
-        //UNREGISTER_CLK_NOTIFIER(pd_lcdc0);
-        //UNREGISTER_CLK_NOTIFIER(pd_lcdc1);
-       }
-
        return ret;
 }
 late_initcall(ddrfreq_init);
+/****************************ddr bandwith tst************************************/
+#if defined(CONFIG_RK_PM_TESTS)
+static ssize_t ddrbw_dyn_show(struct kobject *kobj, struct kobj_attribute *attr,
+               char *buf)
+{
+       char *str = buf;
+       str += sprintf(str, "print: %d\n", print);
+       str += sprintf(str, "watch: %d\n", watch);
+       str += sprintf(str, "high_load: %d\n", high_load);
+       str += sprintf(str, "low_load: %d\n", low_load);
+       str += sprintf(str, "auto_freq_interval_ms: %d\n", auto_freq_interval_ms);
+       str += sprintf(str, "down_rate_delay_ms: %d\n", down_rate_delay_ms);
+//     str += sprintf(str, "low_load_last_ms: %d\n", low_load_last_ms);
+       if (str != buf)
+               *(str - 1) = '\n';
+       return (str - buf);
+}
+
+static ssize_t ddrbw_dyn_store(struct kobject *kobj, struct kobj_attribute *attr,
+               const char *buf, size_t n)
+{
+       int value;
+       char var_name[64];
+
+       sscanf(buf, "%s %u", var_name, &value);
+
+       if((strncmp(buf, "print", strlen("print")) == 0)) {
+               print = value;
+       } else if((strncmp(buf, "watch", strlen("watch")) == 0)) {
+               watch = value;
+       } else if((strncmp(buf, "high", strlen("high")) == 0)) {
+               high_load = value;
+       } else if((strncmp(buf, "low", strlen("low")) == 0)) {
+               low_load = value;
+       } else if((strncmp(buf, "interval", strlen("interval")) == 0)) {
+               auto_freq_interval_ms = value;
+       } else if((strncmp(buf, "downdelay", strlen("downdelay")) == 0)) {
+               down_rate_delay_ms = value;
+       }
+       return n;
+}
+
+struct ddrfreq_attribute {
+       struct attribute        attr;
+       ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
+                       char *buf);
+       ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
+                       const char *buf, size_t n);
+};
+
+static struct ddrfreq_attribute ddrfreq_attrs[] = {
+       /*     node_name        permision               show_func       store_func */    
+       __ATTR(ddrfreq, S_IRUSR|S_IRGRP|S_IWUSR,        ddrbw_dyn_show, ddrbw_dyn_store),
+};
+int rk_pm_tests_kobj_atrradd(const struct attribute *attr);
+
+static void ddrfreq_tst_init(void)
+{
+       int ret;
+
+       ret = rk_pm_tests_kobj_atrradd(&ddrfreq_attrs[0].attr);
+
+       if (ret) {
+               printk("%s: create ddrfreq sysfs node error, ret: %d\n", __func__, ret);
+               return;
+       }
+}
+#endif