misc: ti-st: Adding IORESOURCE_IRQ_OPTIONAL case
[linux-2.6.git] / drivers / misc / tegra-throughput.c
index 926770b..529a7d5 100644 (file)
 
 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
 
-static unsigned short target_frame_time;
-static unsigned short last_frame_time;
+static unsigned int target_frame_time;
 static ktime_t last_flip;
-static unsigned int multiple_app_disable;
 static spinlock_t lock;
 
-#define EMA_PERIOD 16
-#define EMA_SHIFT   4
+#define EMA_PERIOD  8
 
 static int frame_time_sum_init = 1;
 static long frame_time_sum; /* used for fps EMA */
@@ -46,6 +43,9 @@ static long frame_time_sum; /* used for fps EMA */
 static struct work_struct work;
 static int throughput_hint;
 
+static int sync_rate;
+static int throughput_active_app_count;
+
 static void set_throughput_hint(struct work_struct *work)
 {
        /* notify throughput hint clients here */
@@ -57,49 +57,38 @@ static void throughput_flip_callback(void)
        long timediff;
        ktime_t now;
 
-       /* only register flips when a single app is active */
-       if (multiple_app_disable)
-               return NOTIFY_DONE;
-
        now = ktime_get();
 
        if (last_flip.tv64 != 0) {
                timediff = (long) ktime_us_delta(now, last_flip);
 
-               if (timediff > (long) USHRT_MAX)
-                       last_frame_time = USHRT_MAX;
-               else
-                       last_frame_time = (unsigned short) timediff;
-
-               if (last_frame_time == 0) {
+               if (timediff <= 0) {
                        pr_warn("%s: flips %lld nsec apart\n",
                                __func__, now.tv64 - last_flip.tv64);
-                       return NOTIFY_DONE;
+                       last_flip = now;
+                       return;
                }
 
                throughput_hint =
-                       ((int) target_frame_time * 1000) / last_frame_time;
+                       ((int) target_frame_time * 1000) / timediff;
 
-               if (!work_pending(&work))
+               /* only deliver throughput hints when a single app is active */
+               if (throughput_active_app_count == 1 && !work_pending(&work))
                        schedule_work(&work);
 
                if (frame_time_sum_init) {
-                       frame_time_sum = last_frame_time * EMA_PERIOD;
+                       frame_time_sum = timediff * EMA_PERIOD;
                        frame_time_sum_init = 0;
                } else {
-                       int t = frame_time_sum * (EMA_PERIOD - 1);
-                       frame_time_sum = (t >> EMA_SHIFT) + last_frame_time;
+                       int t = (frame_time_sum / EMA_PERIOD) *
+                               (EMA_PERIOD - 1);
+                       frame_time_sum = t + timediff;
                }
        }
 
        last_flip = now;
-
-       return NOTIFY_OK;
 }
 
-static int sync_rate;
-static int throughput_active_app_count;
-
 static void reset_target_frame_time(void)
 {
        if (sync_rate == 0) {
@@ -109,29 +98,18 @@ static void reset_target_frame_time(void)
                        sync_rate = DEFAULT_SYNC_RATE;
        }
 
-       target_frame_time = (unsigned short) (1000000000 / sync_rate);
+       target_frame_time = (unsigned int) (1000000000 / sync_rate);
 
        pr_debug("%s: panel sync rate %d, target frame time %u\n",
                __func__, sync_rate, target_frame_time);
 }
 
-static int callback_initialized;
-
 static int throughput_open(struct inode *inode, struct file *file)
 {
        spin_lock(&lock);
 
-       if (!callback_initialized) {
-               callback_initialized = 1;
-               tegra_dc_set_flip_callback(throughput_flip_callback);
-       }
-
        throughput_active_app_count++;
-       if (throughput_active_app_count > 1) {
-               multiple_app_disable = 1;
-               frame_time_sum_init = 1;
-               frame_time_sum = 0;
-       }
+       frame_time_sum_init = 1;
 
        spin_unlock(&lock);
 
@@ -146,14 +124,10 @@ static int throughput_release(struct inode *inode, struct file *file)
        spin_lock(&lock);
 
        throughput_active_app_count--;
-       if (throughput_active_app_count == 0) {
+       frame_time_sum_init = 1;
+
+       if (throughput_active_app_count == 1)
                reset_target_frame_time();
-               multiple_app_disable = 0;
-               callback_initialized = 0;
-               frame_time_sum_init = 1;
-               frame_time_sum = 0;
-               tegra_dc_unset_flip_callback();
-       }
 
        spin_unlock(&lock);
 
@@ -164,13 +138,9 @@ static int throughput_release(struct inode *inode, struct file *file)
 
 static int throughput_set_target_fps(unsigned long arg)
 {
-       int disable;
-
        pr_debug("%s: target fps %lu requested\n", __func__, arg);
 
-       disable = multiple_app_disable;
-
-       if (disable) {
+       if (throughput_active_app_count != 1) {
                pr_debug("%s: %d active apps, disabling fps usage\n",
                        __func__, throughput_active_app_count);
                return 0;
@@ -178,14 +148,8 @@ static int throughput_set_target_fps(unsigned long arg)
 
        if (arg == 0)
                reset_target_frame_time();
-       else {
-               unsigned long frame_time = (1000000 / arg);
-
-               if (frame_time > USHRT_MAX)
-                       frame_time = USHRT_MAX;
-
-               target_frame_time = (unsigned short) frame_time;
-       }
+       else
+               target_frame_time = (unsigned int) (1000000 / arg);
 
        return 0;
 }
@@ -230,25 +194,31 @@ static struct miscdevice throughput_miscdev = {
        .mode  = 0666,
 };
 
-static int fps_show(struct seq_file *s, void *unused)
+static ssize_t show_fps(struct kobject *kobj,
+       struct attribute *attr, char *buf)
 {
-       int frame_time_avg = frame_time_sum >> EMA_SHIFT;
-       int fps = frame_time_avg > 0 ? 1000000 / frame_time_avg : 0;
-       seq_printf(s, "%d\n", fps);
-       return 0;
-}
+       int frame_time_avg;
+       ktime_t now;
+       long timediff;
+       int fps = 0;
 
-static int fps_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, fps_show, inode->i_private);
+       if (frame_time_sum_init)
+               goto DONE;
+
+       now = ktime_get();
+       timediff = (long) ktime_us_delta(now, last_flip);
+       if (timediff > 1000000)
+               goto DONE;
+
+       frame_time_avg = frame_time_sum / EMA_PERIOD;
+       fps = frame_time_avg > 0 ? 1000000 / frame_time_avg : 0;
+
+DONE:
+       return sprintf(buf, "%d\n", fps);
 }
 
-static const struct file_operations fps_fops = {
-       .open           = fps_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+static struct global_attr fps_attr = __ATTR(fps, 0444,
+               show_fps, NULL);
 
 int __init throughput_init_miscdev(void)
 {
@@ -266,7 +236,12 @@ int __init throughput_init_miscdev(void)
                return ret;
        }
 
-       debugfs_create_file("fps", 0444, NULL, NULL, &fps_fops);
+       ret = sysfs_create_file(&throughput_miscdev.this_device->kobj,
+               &fps_attr.attr);
+       if (ret)
+               pr_err("%s: error %d creating sysfs node\n", __func__, ret);
+
+       tegra_dc_set_flip_callback(throughput_flip_callback);
 
        return 0;
 }
@@ -277,8 +252,12 @@ void __exit throughput_exit_miscdev(void)
 {
        pr_debug("%s: exiting\n", __func__);
 
+       tegra_dc_unset_flip_callback();
+
        cancel_work_sync(&work);
 
+       sysfs_remove_file(&throughput_miscdev.this_device->kobj, &fps_attr.attr);
+
        misc_deregister(&throughput_miscdev);
 }