summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlan Aelion <iaelion@nvidia.com>2012-06-27 18:58:11 -0600
committerLokesh Pathak <lpathak@nvidia.com>2012-07-30 08:37:00 -0700
commite402db9b3737209c9981aea5970328d7c1cb06ce (patch)
tree245efed1639bfb099649c6d81263ea1a7e752cd9
parent8055acfa05c82997bdcfbce5093b9cd963251d2a (diff)
misc: tegra-throughput: adding throughput dev node
Creates a miscdev at /dev/tegra-throughput which gl will use to set a target frame rate. In addition it receives notifications from dc on flip events. On each notification the percentage ratio of the actual frame time to the target frame time is calculated. In subsequent changes this ratio will be reported to other modules as a throughput hint. Bug 991589 Change-Id: Ieaa2b2755b63d2d071de31e3ef819d4c3b51a956 Signed-off-by: Ilan Aelion <iaelion@nvidia.com> Reviewed-on: http://git-master/r/116865 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> Reviewed-by: Jon Mayo <jmayo@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h5
-rw-r--r--drivers/misc/Kconfig7
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/tegra-throughput.c231
-rw-r--r--drivers/video/tegra/dc/dc_priv.h2
-rw-r--r--drivers/video/tegra/dc/ext/dev.c29
-rw-r--r--drivers/video/tegra/dc/mode.c13
-rw-r--r--include/linux/throughput_ioctl.h39
8 files changed, 326 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index 161e37616497..47210c1cd87b 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -25,6 +25,7 @@
#include <linux/pm.h>
#include <linux/types.h>
#include <drm/drm_fixed.h>
+#include <linux/notifier.h>
#define TEGRA_MAX_DC 2
#define DC_N_WINDOWS 3
@@ -584,4 +585,8 @@ struct tegra_dc_edid {
struct tegra_dc_edid *tegra_dc_get_edid(struct tegra_dc *dc);
void tegra_dc_put_edid(struct tegra_dc_edid *edid);
+int tegra_dc_register_flip_notifier(struct notifier_block *nb);
+int tegra_dc_unregister_flip_notifier(struct notifier_block *nb);
+int tegra_dc_get_panel_sync_rate(void);
+
#endif
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index dcf345e23487..0d6210e50e43 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -568,6 +568,13 @@ config THERM_EST
---help---
Thermal driver which estimates temperature based of other sensors.
+config TEGRA_THROUGHPUT
+ bool "Device node to set throughput target"
+ depends on TEGRA_DC && TEGRA_DC_EXTENSIONS
+ default y
+ ---help---
+ Dev node /dev/tegra-throughput used to set a throughput target.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d9172c99bf6d..39d1427ae549 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_TEGRA_BB_SUPPORT) += tegra-baseband/
obj-$(CONFIG_TEGRA_CEC_SUPPORT) += tegra-cec/
obj-$(CONFIG_MAX1749_VIBRATOR) += max1749.o
obj-$(CONFIG_THERM_EST) += therm_est.o
+obj-$(CONFIG_TEGRA_THROUGHPUT) += tegra-throughput.o
diff --git a/drivers/misc/tegra-throughput.c b/drivers/misc/tegra-throughput.c
new file mode 100644
index 000000000000..c909a81d0882
--- /dev/null
+++ b/drivers/misc/tegra-throughput.c
@@ -0,0 +1,231 @@
+/*
+ * drivers/misc/throughput.c
+ *
+ * Copyright (C) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/notifier.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/throughput_ioctl.h>
+#include <mach/dc.h>
+
+#define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
+
+static unsigned short target_frame_time;
+static unsigned short last_frame_time;
+static ktime_t last_flip;
+static unsigned int multiple_app_disable;
+
+static spinlock_t lock;
+
+static int throughput_flip_notifier(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ /* only register flips when a single display is active */
+ if (val != 1 || multiple_app_disable)
+ return NOTIFY_DONE;
+ else {
+ long timediff;
+ ktime_t now;
+ int throughput_hint;
+
+ 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;
+
+ throughput_hint =
+ ((int) target_frame_time * 100)/last_frame_time;
+
+ /* notify throughput hint clients here */
+ }
+ last_flip = now;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block throughput_flip_nb = {
+ .notifier_call = throughput_flip_notifier,
+};
+
+static int sync_rate;
+static int throughput_active_app_count;
+
+static void reset_target_frame_time(void)
+{
+ if (sync_rate == 0) {
+ sync_rate = tegra_dc_get_panel_sync_rate();
+
+ if (sync_rate == 0)
+ sync_rate = DEFAULT_SYNC_RATE;
+ }
+
+ target_frame_time = (unsigned short) (1000000000 / sync_rate);
+
+ pr_debug("%s: panel sync rate %d, target frame time %u\n",
+ __func__, sync_rate, target_frame_time);
+}
+
+static int notifier_initialized;
+
+static int throughput_open(struct inode *inode, struct file *file)
+{
+ if (!notifier_initialized) {
+ tegra_dc_register_flip_notifier(&throughput_flip_nb);
+ notifier_initialized = 1;
+ }
+
+ spin_lock(&lock);
+
+ throughput_active_app_count++;
+ if (throughput_active_app_count > 1)
+ multiple_app_disable = 1;
+
+ spin_unlock(&lock);
+
+ pr_debug("throughput_open node %p file %p\n", inode, file);
+
+ return 0;
+}
+
+static int throughput_release(struct inode *inode, struct file *file)
+{
+ spin_lock(&lock);
+ throughput_active_app_count--;
+ spin_unlock(&lock);
+
+ if (throughput_active_app_count == 0) {
+ reset_target_frame_time();
+ multiple_app_disable = 0;
+ tegra_dc_unregister_flip_notifier(&throughput_flip_nb);
+ notifier_initialized = 0;
+ }
+
+
+ pr_debug("throughput_release node %p file %p\n", inode, file);
+
+ return 0;
+}
+
+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) {
+ pr_debug("%s: %d active apps, disabling fps usage\n",
+ __func__, throughput_active_app_count);
+ return 0;
+ }
+
+ 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;
+ }
+
+ return 0;
+}
+
+static long
+throughput_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+
+ if ((_IOC_TYPE(cmd) != TEGRA_THROUGHPUT_MAGIC) ||
+ (_IOC_NR(cmd) == 0) ||
+ (_IOC_NR(cmd) > TEGRA_THROUGHPUT_IOCTL_MAXNR))
+ return -EFAULT;
+
+ switch (cmd) {
+ case TEGRA_THROUGHPUT_IOCTL_TARGET_FPS:
+ pr_debug("%s: TEGRA_THROUGHPUT_IOCTL_TARGET_FPS %lu\n",
+ __func__, arg);
+ err = throughput_set_target_fps(arg);
+ break;
+
+ default:
+ err = -ENOTTY;
+ }
+
+ return err;
+}
+
+static const struct file_operations throughput_user_fops = {
+ .owner = THIS_MODULE,
+ .open = throughput_open,
+ .release = throughput_release,
+ .unlocked_ioctl = throughput_ioctl,
+};
+
+#define TEGRA_THROUGHPUT_MINOR 1
+
+static struct miscdevice throughput_miscdev = {
+ .minor = TEGRA_THROUGHPUT_MINOR,
+ .name = "tegra-throughput",
+ .fops = &throughput_user_fops,
+ .mode = 0666,
+};
+
+int __init throughput_init_miscdev(void)
+{
+ int ret;
+
+ pr_debug("%s: initializing\n", __func__);
+
+ spin_lock_init(&lock);
+
+ ret = misc_register(&throughput_miscdev);
+ if (ret) {
+ pr_err("can\'t reigster throughput miscdev"
+ " (minor %d err %d)\n", TEGRA_THROUGHPUT_MINOR, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+module_init(throughput_init_miscdev);
+
+void __exit throughput_exit_miscdev(void)
+{
+ pr_debug("%s: exiting\n", __func__);
+
+ misc_deregister(&throughput_miscdev);
+}
+
+module_exit(throughput_exit_miscdev);
+
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 332c80f9cbb6..759d64da7052 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -372,7 +372,7 @@ void tegra_dc_disable_crc(struct tegra_dc *dc);
void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
const struct tegra_dc_out_pin *pins,
const unsigned int n_pins);
-/* defined in dc.c, used in bandwidth.c */
+/* defined in dc.c, used in bandwidth.c and ext/dev.c */
unsigned int tegra_dc_has_multiple_dc(void);
/* defined in dc.c, used in dsi.c */
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c
index f9c76f8f0d0d..92e42ce32ac2 100644
--- a/drivers/video/tegra/dc/ext/dev.c
+++ b/drivers/video/tegra/dc/ext/dev.c
@@ -274,6 +274,32 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
return 0;
}
+static struct srcu_notifier_head tegra_dc_flip_notifier_list;
+static bool init_tegra_dc_flip_notifier_list_called;
+static int __init init_tegra_dc_flip_notifier_list(void)
+{
+ srcu_init_notifier_head(&tegra_dc_flip_notifier_list);
+ init_tegra_dc_flip_notifier_list_called = true;
+ return 0;
+}
+
+pure_initcall(init_tegra_dc_flip_notifier_list);
+
+int tegra_dc_register_flip_notifier(struct notifier_block *nb)
+{
+ WARN_ON(!init_tegra_dc_flip_notifier_list_called);
+
+ return srcu_notifier_chain_register(
+ &tegra_dc_flip_notifier_list, nb);
+}
+EXPORT_SYMBOL(tegra_dc_register_flip_notifier);
+
+int tegra_dc_unregister_flip_notifier(struct notifier_block *nb)
+{
+ return srcu_notifier_chain_unregister(&tegra_dc_flip_notifier_list, nb);
+}
+EXPORT_SYMBOL(tegra_dc_unregister_flip_notifier);
+
static void tegra_dc_ext_flip_worker(struct work_struct *work)
{
struct tegra_dc_ext_flip_data *data =
@@ -327,6 +353,9 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work)
tegra_dc_update_windows(wins, nr_win);
/* TODO: implement swapinterval here */
tegra_dc_sync_windows(wins, nr_win);
+ if (!tegra_dc_has_multiple_dc())
+ srcu_notifier_call_chain(&tegra_dc_flip_notifier_list,
+ 1UL, NULL);
}
for (i = 0; i < DC_N_WINDOWS; i++) {
diff --git a/drivers/video/tegra/dc/mode.c b/drivers/video/tegra/dc/mode.c
index 49cc5f5abd53..be909691b957 100644
--- a/drivers/video/tegra/dc/mode.c
+++ b/drivers/video/tegra/dc/mode.c
@@ -247,10 +247,23 @@ int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
return 0;
}
+static int panel_sync_rate;
+
+int tegra_dc_get_panel_sync_rate(void)
+{
+ return panel_sync_rate;
+}
+EXPORT_SYMBOL(tegra_dc_get_panel_sync_rate);
+
int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
{
memcpy(&dc->mode, mode, sizeof(dc->mode));
+ if (dc->out->type == TEGRA_DC_OUT_RGB)
+ panel_sync_rate = tegra_dc_calc_refresh(mode);
+ else if (dc->out->type == TEGRA_DC_OUT_DSI)
+ panel_sync_rate = dc->out->dsi->rated_refresh_rate * 1000;
+
print_mode(dc, mode, __func__);
return 0;
diff --git a/include/linux/throughput_ioctl.h b/include/linux/throughput_ioctl.h
new file mode 100644
index 000000000000..96e57399b2e9
--- /dev/null
+++ b/include/linux/throughput_ioctl.h
@@ -0,0 +1,39 @@
+/*
+ * include/linux/throughput_ioctl.h
+ *
+ * ioctl declarations for throughput miscdev
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __TEGRA_THROUGHPUT_IOCTL_H
+#define __TEGRA_THROUGHPUT_IOCTL_H
+
+#include <linux/ioctl.h>
+
+#define TEGRA_THROUGHPUT_MAGIC 'g'
+
+struct tegra_throughput_target_fps_args {
+ __u32 target_fps;
+};
+
+#define TEGRA_THROUGHPUT_IOCTL_TARGET_FPS \
+ _IOW(TEGRA_THROUGHPUT_MAGIC, 1, struct tegra_throughput_target_fps_args)
+#define TEGRA_THROUGHPUT_IOCTL_MAXNR \
+ (_IOC_NR(TEGRA_THROUGHPUT_IOCTL_TARGET_FPS))
+
+#endif /* !defined(__TEGRA_THROUGHPUT_IOCTL_H) */
+