2 * drivers/misc/throughput.c
4 * Copyright (C) 2012, NVIDIA CORPORATION. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 of the License.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <linux/kthread.h>
21 #include <linux/ktime.h>
22 #include <linux/notifier.h>
23 #include <linux/miscdevice.h>
25 #include <linux/init.h>
26 #include <linux/spinlock.h>
27 #include <linux/throughput_ioctl.h>
28 #include <linux/module.h>
29 #include <linux/nvhost.h>
32 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
34 static unsigned short target_frame_time;
35 static unsigned short last_frame_time;
36 static ktime_t last_flip;
37 static unsigned int multiple_app_disable;
39 static spinlock_t lock;
41 static int throughput_flip_notifier(struct notifier_block *nb,
45 /* only register flips when a single display is active */
46 if (val != 1 || multiple_app_disable)
54 if (last_flip.tv64 != 0) {
55 timediff = (long) ktime_us_delta(now, last_flip);
56 if (timediff > (long) USHRT_MAX)
57 last_frame_time = USHRT_MAX;
59 last_frame_time = (unsigned short) timediff;
61 if (last_frame_time == 0)
65 ((int) target_frame_time * 100)/last_frame_time;
67 /* notify throughput hint clients here */
68 nvhost_scale3d_set_throughput_hint(throughput_hint);
76 static struct notifier_block throughput_flip_nb = {
77 .notifier_call = throughput_flip_notifier,
81 static int throughput_active_app_count;
83 static void reset_target_frame_time(void)
86 sync_rate = tegra_dc_get_panel_sync_rate();
89 sync_rate = DEFAULT_SYNC_RATE;
92 target_frame_time = (unsigned short) (1000000000 / sync_rate);
94 pr_debug("%s: panel sync rate %d, target frame time %u\n",
95 __func__, sync_rate, target_frame_time);
98 static int notifier_initialized;
100 static int throughput_open(struct inode *inode, struct file *file)
106 if (!notifier_initialized) {
107 notifier_initialized = 1;
111 throughput_active_app_count++;
112 if (throughput_active_app_count > 1)
113 multiple_app_disable = 1;
118 tegra_dc_register_flip_notifier(&throughput_flip_nb);
120 pr_debug("throughput_open node %p file %p\n", inode, file);
125 static int throughput_release(struct inode *inode, struct file *file)
131 throughput_active_app_count--;
132 if (throughput_active_app_count == 0) {
133 reset_target_frame_time();
134 multiple_app_disable = 0;
135 notifier_initialized = 0;
142 tegra_dc_unregister_flip_notifier(&throughput_flip_nb);
144 pr_debug("throughput_release node %p file %p\n", inode, file);
149 static int throughput_set_target_fps(unsigned long arg)
153 pr_debug("%s: target fps %lu requested\n", __func__, arg);
155 disable = multiple_app_disable;
158 pr_debug("%s: %d active apps, disabling fps usage\n",
159 __func__, throughput_active_app_count);
164 reset_target_frame_time();
166 unsigned long frame_time = (1000000 / arg);
168 if (frame_time > USHRT_MAX)
169 frame_time = USHRT_MAX;
171 target_frame_time = (unsigned short) frame_time;
178 throughput_ioctl(struct file *file,
184 if ((_IOC_TYPE(cmd) != TEGRA_THROUGHPUT_MAGIC) ||
185 (_IOC_NR(cmd) == 0) ||
186 (_IOC_NR(cmd) > TEGRA_THROUGHPUT_IOCTL_MAXNR))
190 case TEGRA_THROUGHPUT_IOCTL_TARGET_FPS:
191 pr_debug("%s: TEGRA_THROUGHPUT_IOCTL_TARGET_FPS %lu\n",
193 err = throughput_set_target_fps(arg);
203 static const struct file_operations throughput_user_fops = {
204 .owner = THIS_MODULE,
205 .open = throughput_open,
206 .release = throughput_release,
207 .unlocked_ioctl = throughput_ioctl,
210 #define TEGRA_THROUGHPUT_MINOR 1
212 static struct miscdevice throughput_miscdev = {
213 .minor = TEGRA_THROUGHPUT_MINOR,
214 .name = "tegra-throughput",
215 .fops = &throughput_user_fops,
219 int __init throughput_init_miscdev(void)
223 pr_debug("%s: initializing\n", __func__);
225 spin_lock_init(&lock);
227 ret = misc_register(&throughput_miscdev);
229 pr_err("can\'t reigster throughput miscdev"
230 " (minor %d err %d)\n", TEGRA_THROUGHPUT_MINOR, ret);
237 module_init(throughput_init_miscdev);
239 void __exit throughput_exit_miscdev(void)
241 pr_debug("%s: exiting\n", __func__);
243 misc_deregister(&throughput_miscdev);
246 module_exit(throughput_exit_miscdev);