summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Cherry <tcherry@nvidia.com>2012-06-07 22:47:04 -0700
committerTom Cherry <tcherry@nvidia.com>2012-06-07 22:47:04 -0700
commit94596e0f2e5485acce0d6eef11799c5c35610cd5 (patch)
tree8b53e2a7700aad78e278288f5aed6d95f514bc23 /drivers
parent7ca2a5f1d750b580f5093c2c6895772b32d30f66 (diff)
parentb17f01f41b5cd2a49e48b87fb51d59c181819d89 (diff)
Merge remote branch 'origin/android-tegra-nv-3.1' into HEAD
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/cpuquiet/Kconfig11
-rw-r--r--drivers/cpuquiet/Makefile1
-rw-r--r--drivers/cpuquiet/cpuquiet.c32
-rw-r--r--drivers/cpuquiet/cpuquiet.h36
-rw-r--r--drivers/cpuquiet/driver.c200
-rw-r--r--drivers/cpuquiet/governor.c101
-rw-r--r--drivers/cpuquiet/governors/Makefile1
-rw-r--r--drivers/cpuquiet/governors/balanced.c473
-rw-r--r--drivers/cpuquiet/governors/userspace.c56
-rw-r--r--drivers/cpuquiet/sysfs.c290
-rw-r--r--drivers/media/video/tegra/Makefile2
-rw-r--r--drivers/media/video/tegra/ad5816.c22
-rw-r--r--drivers/media/video/tegra/nvavp/nvavp_dev.c6
-rw-r--r--drivers/media/video/tegra/ov5640.c2
-rw-r--r--drivers/media/video/tegra/tegra_camera.c11
-rw-r--r--drivers/misc/bcm4329_rfkill.c8
-rw-r--r--drivers/tty/serial/tegra_hsuart.c2
-rw-r--r--drivers/usb/otg/tegra-otg.c44
-rw-r--r--drivers/video/tegra/dc/Makefile1
-rw-r--r--drivers/video/tegra/dc/bandwidth.c275
-rw-r--r--drivers/video/tegra/dc/dc.c351
-rw-r--r--drivers/video/tegra/dc/dc_config.h14
-rw-r--r--drivers/video/tegra/dc/dc_priv.h88
-rw-r--r--drivers/video/tegra/dc/hdmi.c16
25 files changed, 1652 insertions, 392 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 4cbb7ce0a0f9..d7f4b63904ac 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -5,6 +5,7 @@
# Rewritten to use lists instead of if-statements.
#
+obj-$(CONFIG_CPUQUIET_FRAMEWORK)+= cpuquiet/
obj-y += gpio/
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_PARISC) += parisc/
diff --git a/drivers/cpuquiet/Kconfig b/drivers/cpuquiet/Kconfig
new file mode 100644
index 000000000000..844cd34a69b3
--- /dev/null
+++ b/drivers/cpuquiet/Kconfig
@@ -0,0 +1,11 @@
+menu "CPUQUIET Framework"
+
+config CPUQUIET_FRAMEWORK
+ bool "Cpuquiet framework"
+ default n
+ help
+ Cpuquiet implements pluggable policies for forcing cpu cores into a
+ quiescent state. Appropriate policies will save power without hurting
+ performance.
+
+endmenu
diff --git a/drivers/cpuquiet/Makefile b/drivers/cpuquiet/Makefile
new file mode 100644
index 000000000000..0502d4f33012
--- /dev/null
+++ b/drivers/cpuquiet/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CPUQUIET_FRAMEWORK) += cpuquiet.o driver.o sysfs.o governor.o governors/
diff --git a/drivers/cpuquiet/cpuquiet.c b/drivers/cpuquiet/cpuquiet.c
new file mode 100644
index 000000000000..d902af26c8d7
--- /dev/null
+++ b/drivers/cpuquiet/cpuquiet.c
@@ -0,0 +1,32 @@
+/*
+ * 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/kernel.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/cpuquiet.h>
+#include "cpuquiet.h"
+
+DEFINE_MUTEX(cpuquiet_lock);
+
+static int __init cpuquiet_init(void)
+{
+ return cpuquiet_add_class_sysfs(&cpu_sysdev_class);
+}
+
+core_initcall(cpuquiet_init);
diff --git a/drivers/cpuquiet/cpuquiet.h b/drivers/cpuquiet/cpuquiet.h
new file mode 100644
index 000000000000..fa61946ff119
--- /dev/null
+++ b/drivers/cpuquiet/cpuquiet.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef __DRIVER_CPUQUIET_H
+#define __DRIVER_CPUQUIET_H
+
+#include <linux/sysdev.h>
+
+extern struct mutex cpuquiet_lock;
+extern struct cpuquiet_governor *cpuquiet_curr_governor;
+extern struct list_head cpuquiet_governors;
+int cpuquiet_add_class_sysfs(struct sysdev_class *cls);
+struct cpuquiet_governor *cpuquiet_find_governor(const char *str);
+int cpuquiet_switch_governor(struct cpuquiet_governor *gov);
+struct cpuquiet_governor *cpuquiet_get_first_governor(void);
+struct cpuquiet_driver *cpuquiet_get_driver(void);
+void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu);
+void cpuquiet_remove_dev(unsigned int cpu);
+int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name, int cpu);
+#endif
diff --git a/drivers/cpuquiet/driver.c b/drivers/cpuquiet/driver.c
new file mode 100644
index 000000000000..f9dcdf018f58
--- /dev/null
+++ b/drivers/cpuquiet/driver.c
@@ -0,0 +1,200 @@
+/*
+ * 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/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <asm/cputime.h>
+
+#include "cpuquiet.h"
+
+struct cpuquiet_cpu_stat {
+ cputime64_t time_up_total;
+ u64 last_update;
+ unsigned int up_down_count;
+ struct kobject cpu_kobject;
+};
+
+struct cpu_attribute {
+ struct attribute attr;
+ enum { up_down_count, time_up_total } type;
+};
+
+static struct cpuquiet_driver *cpuquiet_curr_driver;
+struct cpuquiet_cpu_stat *stats;
+
+#define CPU_ATTRIBUTE(_name) \
+ static struct cpu_attribute _name ## _attr = { \
+ .attr = {.name = __stringify(_name), .mode = 0444 }, \
+ .type = _name, \
+}
+
+CPU_ATTRIBUTE(up_down_count);
+CPU_ATTRIBUTE(time_up_total);
+
+static struct attribute *cpu_attributes[] = {
+ &up_down_count_attr.attr,
+ &time_up_total_attr.attr,
+ NULL,
+};
+
+static void stats_update(struct cpuquiet_cpu_stat *stat, bool up)
+{
+ u64 cur_jiffies = get_jiffies_64();
+ bool was_up = stat->up_down_count & 0x1;
+
+ if (was_up)
+ stat->time_up_total = cputime64_add(stat->time_up_total,
+ cputime64_sub(cur_jiffies, stat->last_update));
+
+ if (was_up != up)
+ stat->up_down_count++;
+
+ stat->last_update = cur_jiffies;
+}
+
+int cpuquiet_quiesence_cpu(unsigned int cpunumber)
+{
+ int err = -EPERM;
+
+ if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu)
+ err = cpuquiet_curr_driver->quiesence_cpu(cpunumber);
+
+ stats_update(stats + cpunumber, 0);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_quiesence_cpu);
+
+int cpuquiet_wake_cpu(unsigned int cpunumber)
+{
+ int err = -EPERM;
+
+ if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu)
+ err = cpuquiet_curr_driver->wake_cpu(cpunumber);
+
+ stats_update(stats + cpunumber, 1);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_wake_cpu);
+
+static ssize_t stats_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpu_attribute *cattr =
+ container_of(attr, struct cpu_attribute, attr);
+ struct cpuquiet_cpu_stat *stat =
+ container_of(kobj, struct cpuquiet_cpu_stat, cpu_kobject);
+ ssize_t len = 0;
+ bool was_up = stat->up_down_count & 0x1;
+
+ stats_update(stat, was_up);
+
+ switch (cattr->type) {
+ case up_down_count:
+ len = sprintf(buf, "%u\n", stat->up_down_count);
+ break;
+ case time_up_total:
+ len = sprintf(buf, "%llu\n", stat->time_up_total);
+ break;
+ }
+
+ return len;
+}
+
+static const struct sysfs_ops stats_sysfs_ops = {
+ .show = stats_sysfs_show,
+};
+
+static struct kobj_type ktype_cpu_stats = {
+ .sysfs_ops = &stats_sysfs_ops,
+ .default_attrs = cpu_attributes,
+};
+
+int cpuquiet_register_driver(struct cpuquiet_driver *drv)
+{
+ int err = -EBUSY;
+ unsigned int cpu;
+ struct sys_device *sys_dev;
+ u64 cur_jiffies;
+
+ if (!drv)
+ return -EINVAL;
+
+ stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL);
+ if (!stats)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ cur_jiffies = get_jiffies_64();
+ stats[cpu].last_update = cur_jiffies;
+ if (cpu_online(cpu))
+ stats[cpu].up_down_count = 1;
+ sys_dev = get_cpu_sysdev(cpu);
+ if (sys_dev) {
+ cpuquiet_add_dev(sys_dev, cpu);
+ cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject,
+ &ktype_cpu_stats, "stats", cpu);
+ }
+ }
+
+ mutex_lock(&cpuquiet_lock);
+ if (!cpuquiet_curr_driver) {
+ err = 0;
+ cpuquiet_curr_driver = drv;
+ cpuquiet_switch_governor(cpuquiet_get_first_governor());
+ }
+ mutex_unlock(&cpuquiet_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_register_driver);
+
+struct cpuquiet_driver *cpuquiet_get_driver(void)
+{
+ return cpuquiet_curr_driver;
+}
+
+void cpuquiet_unregister_driver(struct cpuquiet_driver *drv)
+{
+ unsigned int cpu;
+
+ if (drv != cpuquiet_curr_driver) {
+ WARN(1, "invalid cpuquiet_unregister_driver(%s)\n",
+ drv->name);
+ return;
+ }
+
+ /* stop current governor first */
+ cpuquiet_switch_governor(NULL);
+
+ mutex_lock(&cpuquiet_lock);
+ cpuquiet_curr_driver = NULL;
+
+ for_each_possible_cpu(cpu) {
+ kobject_put(&stats[cpu].cpu_kobject);
+ cpuquiet_remove_dev(cpu);
+ }
+
+ mutex_unlock(&cpuquiet_lock);
+}
+EXPORT_SYMBOL(cpuquiet_unregister_driver);
diff --git a/drivers/cpuquiet/governor.c b/drivers/cpuquiet/governor.c
new file mode 100644
index 000000000000..1446b9ee5066
--- /dev/null
+++ b/drivers/cpuquiet/governor.c
@@ -0,0 +1,101 @@
+/*
+ * 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/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+
+#include "cpuquiet.h"
+
+LIST_HEAD(cpuquiet_governors);
+struct cpuquiet_governor *cpuquiet_curr_governor;
+
+struct cpuquiet_governor *cpuquiet_get_first_governor(void)
+{
+ if (!list_empty(&cpuquiet_governors))
+ return list_entry(&cpuquiet_governors, struct cpuquiet_governor,
+ governor_list);
+ else
+ return NULL;
+}
+
+struct cpuquiet_governor *cpuquiet_find_governor(const char *str)
+{
+ struct cpuquiet_governor *gov;
+
+ list_for_each_entry(gov, &cpuquiet_governors, governor_list)
+ if (!strnicmp(str, gov->name, CPUQUIET_NAME_LEN))
+ return gov;
+
+ return NULL;
+}
+
+int cpuquiet_switch_governor(struct cpuquiet_governor *gov)
+{
+ int err = 0;
+
+ if (cpuquiet_curr_governor) {
+ if (cpuquiet_curr_governor->stop)
+ cpuquiet_curr_governor->stop();
+ module_put(cpuquiet_curr_governor->owner);
+ }
+
+ cpuquiet_curr_governor = gov;
+
+ if (gov) {
+ if (!try_module_get(cpuquiet_curr_governor->owner))
+ return -EINVAL;
+ if (gov->start)
+ err = gov->start();
+ if (!err)
+ cpuquiet_curr_governor = gov;
+ }
+
+ return err;
+}
+
+int cpuquiet_register_governor(struct cpuquiet_governor *gov)
+{
+ int ret = -EEXIST;
+
+ if (!gov)
+ return -EINVAL;
+
+ mutex_lock(&cpuquiet_lock);
+ if (cpuquiet_find_governor(gov->name) == NULL) {
+ ret = 0;
+ list_add_tail(&gov->governor_list, &cpuquiet_governors);
+ if (!cpuquiet_curr_governor && cpuquiet_get_driver())
+ cpuquiet_switch_governor(gov);
+ }
+ mutex_unlock(&cpuquiet_lock);
+
+ return ret;
+}
+
+void cpuquiet_unregister_governor(struct cpuquiet_governor *gov)
+{
+ if (!gov)
+ return;
+
+ mutex_lock(&cpuquiet_lock);
+ if (cpuquiet_curr_governor == gov)
+ cpuquiet_switch_governor(NULL);
+ list_del(&gov->governor_list);
+ mutex_unlock(&cpuquiet_lock);
+}
diff --git a/drivers/cpuquiet/governors/Makefile b/drivers/cpuquiet/governors/Makefile
new file mode 100644
index 000000000000..c70803127082
--- /dev/null
+++ b/drivers/cpuquiet/governors/Makefile
@@ -0,0 +1 @@
+obj-y += userspace.o balanced.o
diff --git a/drivers/cpuquiet/governors/balanced.c b/drivers/cpuquiet/governors/balanced.c
new file mode 100644
index 000000000000..813a32e671d7
--- /dev/null
+++ b/drivers/cpuquiet/governors/balanced.c
@@ -0,0 +1,473 @@
+/*
+ * 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/kernel.h>
+#include <linux/cpuquiet.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/pm_qos_params.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <asm/cputime.h>
+
+#define CPUNAMELEN 8
+
+typedef enum {
+ CPU_SPEED_BALANCED,
+ CPU_SPEED_BIASED,
+ CPU_SPEED_SKEWED,
+} CPU_SPEED_BALANCE;
+
+typedef enum {
+ IDLE,
+ DOWN,
+ UP,
+} BALANCED_STATE;
+
+struct idle_info {
+ u64 idle_last;
+ u64 last_timestamp;
+ u64 idle_current;
+ u64 timestamp;
+};
+
+static DEFINE_PER_CPU(struct idle_info, idleinfo);
+static DEFINE_PER_CPU(unsigned int, cpu_load);
+
+static struct timer_list load_timer;
+static bool load_timer_active;
+struct balanced_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct balanced_attribute *attr, char *buf);
+ ssize_t (*store)(struct balanced_attribute *attr, const char *buf,
+ size_t count);
+ unsigned long *param;
+};
+
+#define BALANCED_ATTRIBUTE(_name, _mode) \
+ static struct balanced_attribute _name ## _attr = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = show_attribute, \
+ .store = store_attribute, \
+ .param = &_name, \
+}
+
+/* configurable parameters */
+static unsigned long balance_level = 75;
+static unsigned long idle_bottom_freq;
+static unsigned long idle_top_freq;
+static unsigned long up_delay;
+static unsigned long down_delay;
+
+static struct workqueue_struct *balanced_wq;
+static struct delayed_work balanced_work;
+static BALANCED_STATE balanced_state;
+static struct kobject *balanced_kobject;
+
+static void calculate_load_timer(unsigned long data)
+{
+ int i;
+ u64 idle_time, elapsed_time;
+
+ if (!load_timer_active)
+ return;
+
+ for_each_online_cpu(i) {
+ struct idle_info *iinfo = &per_cpu(idleinfo, i);
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ iinfo->idle_last = iinfo->idle_current;
+ iinfo->last_timestamp = iinfo->timestamp;
+ iinfo->idle_current =
+ get_cpu_idle_time_us(i, &iinfo->timestamp);
+ elapsed_time = iinfo->timestamp - iinfo->last_timestamp;
+
+ idle_time = iinfo->idle_current - iinfo->idle_last;
+ idle_time *= 100;
+ do_div(idle_time, elapsed_time);
+ *load = 100 - idle_time;
+ }
+ mod_timer(&load_timer, jiffies + msecs_to_jiffies(100));
+}
+
+static void start_load_timer(void)
+{
+ int i;
+
+ if (load_timer_active)
+ return;
+
+ load_timer_active = true;
+
+ for_each_online_cpu(i) {
+ struct idle_info *iinfo = &per_cpu(idleinfo, i);
+
+ iinfo->idle_current =
+ get_cpu_idle_time_us(i, &iinfo->timestamp);
+ }
+ mod_timer(&load_timer, jiffies + msecs_to_jiffies(100));
+}
+
+static void stop_load_timer(void)
+{
+ if (!load_timer_active)
+ return;
+
+ load_timer_active = false;
+ del_timer(&load_timer);
+}
+
+static unsigned int get_slowest_cpu_n(void)
+{
+ unsigned int cpu = nr_cpu_ids;
+ unsigned long minload = ULONG_MAX;
+ int i;
+
+ for_each_online_cpu(i) {
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ if ((i > 0) && (minload > *load)) {
+ cpu = i;
+ minload = *load;
+ }
+ }
+
+ return cpu;
+}
+
+static unsigned int cpu_highest_speed(void)
+{
+ unsigned int maxload = 0;
+ int i;
+
+ for_each_online_cpu(i) {
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ maxload = max(maxload, *load);
+ }
+
+ return maxload;
+}
+
+static unsigned int count_slow_cpus(unsigned int limit)
+{
+ unsigned int cnt = 0;
+ int i;
+
+ for_each_online_cpu(i) {
+ unsigned int *load = &per_cpu(cpu_load, i);
+
+ if (*load <= limit)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static CPU_SPEED_BALANCE balanced_speed_balance(void)
+{
+ unsigned long highest_speed = cpu_highest_speed();
+ unsigned long balanced_speed = highest_speed * balance_level / 100;
+ unsigned long skewed_speed = balanced_speed / 2;
+ unsigned int nr_cpus = num_online_cpus();
+ unsigned int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
+
+ /* balanced: freq targets for all CPUs are above 50% of highest speed
+ biased: freq target for at least one CPU is below 50% threshold
+ skewed: freq targets for at least 2 CPUs are below 25% threshold */
+ if (count_slow_cpus(skewed_speed) >= 2 || nr_cpus > max_cpus)
+ return CPU_SPEED_SKEWED;
+
+ if (count_slow_cpus(balanced_speed) >= 1 || nr_cpus == max_cpus)
+ return CPU_SPEED_BIASED;
+
+ return CPU_SPEED_BALANCED;
+}
+
+static void balanced_work_func(struct work_struct *work)
+{
+ bool up = false;
+ unsigned int cpu = nr_cpu_ids;
+ CPU_SPEED_BALANCE balance;
+
+ switch (balanced_state) {
+ case IDLE:
+ break;
+ case DOWN:
+ cpu = get_slowest_cpu_n();
+ if (cpu < nr_cpu_ids) {
+ up = false;
+ queue_delayed_work(balanced_wq,
+ &balanced_work, down_delay);
+ } else
+ stop_load_timer();
+ break;
+ case UP:
+ balance = balanced_speed_balance();
+ switch (balance) {
+
+ /* cpu speed is up and balanced - one more on-line */
+ case CPU_SPEED_BALANCED:
+ cpu = cpumask_next_zero(0, cpu_online_mask);
+ if (cpu < nr_cpu_ids)
+ up = true;
+ break;
+ /* cpu speed is up, but skewed - remove one core */
+ case CPU_SPEED_SKEWED:
+ cpu = get_slowest_cpu_n();
+ if (cpu < nr_cpu_ids)
+ up = false;
+ break;
+ /* cpu speed is up, but under-utilized - do nothing */
+ case CPU_SPEED_BIASED:
+ default:
+ break;
+ }
+ queue_delayed_work(
+ balanced_wq, &balanced_work, up_delay);
+ break;
+ default:
+ pr_err("%s: invalid cpuquiet balanced governor state %d\n",
+ __func__, balanced_state);
+ }
+
+ if (cpu < nr_cpu_ids) {
+ if (up)
+ cpuquiet_wake_cpu(cpu);
+ else
+ cpuquiet_quiesence_cpu(cpu);
+ }
+}
+
+static int balanced_cpufreq_transition(struct notifier_block *nb,
+ unsigned long state, void *data)
+{
+ struct cpufreq_freqs *freqs = data;
+ unsigned long cpu_freq;
+
+ if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) {
+ cpu_freq = freqs->new;
+
+ switch (balanced_state) {
+ case IDLE:
+ if (cpu_freq > idle_top_freq) {
+ balanced_state = UP;
+ queue_delayed_work(
+ balanced_wq, &balanced_work, up_delay);
+ start_load_timer();
+ } else if (cpu_freq <= idle_bottom_freq) {
+ balanced_state = DOWN;
+ queue_delayed_work(
+ balanced_wq, &balanced_work,
+ down_delay);
+ start_load_timer();
+ }
+ break;
+ case DOWN:
+ if (cpu_freq > idle_top_freq) {
+ balanced_state = UP;
+ queue_delayed_work(
+ balanced_wq, &balanced_work, up_delay);
+ start_load_timer();
+ }
+ break;
+ case UP:
+ if (cpu_freq <= idle_bottom_freq) {
+ balanced_state = DOWN;
+ queue_delayed_work(balanced_wq,
+ &balanced_work, down_delay);
+ start_load_timer();
+ }
+ break;
+ default:
+ pr_err("%s: invalid tegra hotplug state %d\n",
+ __func__, balanced_state);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block balanced_cpufreq_nb = {
+ .notifier_call = balanced_cpufreq_transition,
+};
+
+static ssize_t show_attribute(struct balanced_attribute *battr, char *buf)
+{
+ return sprintf(buf, "%lu\n", *(battr->param));
+}
+
+static ssize_t store_attribute(struct balanced_attribute *battr,
+ const char *buf, size_t count)
+{
+ int err;
+ unsigned long val;
+
+ err = strict_strtoul(buf, 0, &val);
+ if (err < 0)
+ return err;
+
+ *(battr->param) = val;
+
+ return count;
+}
+
+static ssize_t balanced_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct balanced_attribute *battr =
+ container_of(attr, struct balanced_attribute, attr);
+
+ if (battr->store)
+ return battr->store(battr, buf, count);
+
+ return -EINVAL;
+}
+
+static ssize_t balanced_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct balanced_attribute *battr =
+ container_of(attr, struct balanced_attribute, attr);
+
+ return battr->show(battr, buf);
+}
+
+BALANCED_ATTRIBUTE(balance_level, 0644);
+BALANCED_ATTRIBUTE(idle_bottom_freq, 0644);
+BALANCED_ATTRIBUTE(idle_top_freq, 0644);
+BALANCED_ATTRIBUTE(up_delay, 0644);
+BALANCED_ATTRIBUTE(down_delay, 0644);
+
+static struct attribute *balanced_attributes[] = {
+ &balance_level_attr.attr,
+ &idle_bottom_freq_attr.attr,
+ &idle_top_freq_attr.attr,
+ &up_delay_attr.attr,
+ &down_delay_attr.attr,
+ NULL,
+};
+
+static const struct sysfs_ops balanced_sysfs_ops = {
+ .show = balanced_sysfs_show,
+ .store = balanced_sysfs_store,
+};
+
+static struct kobj_type ktype_balanced = {
+ .sysfs_ops = &balanced_sysfs_ops,
+ .default_attrs = balanced_attributes,
+};
+
+static int balanced_sysfs(void)
+{
+ int err;
+
+ balanced_kobject = kzalloc(sizeof(*balanced_kobject),
+ GFP_KERNEL);
+
+ if (!balanced_kobject)
+ return -ENOMEM;
+
+ err = cpuquiet_kobject_init(balanced_kobject, &ktype_balanced,
+ "balanced");
+
+ if (err)
+ kfree(balanced_kobject);
+
+ return err;
+}
+
+static void balanced_stop(void)
+{
+
+ /*
+ first unregister the notifiers. This ensures the governor state
+ can't be modified by a cpufreq transition
+ */
+ cpufreq_unregister_notifier(&balanced_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ /* now we can force the governor to be idle */
+ balanced_state = IDLE;
+ cancel_delayed_work_sync(&balanced_work);
+ destroy_workqueue(balanced_wq);
+ del_timer(&load_timer);
+
+ kobject_put(balanced_kobject);
+}
+
+static int balanced_start(void)
+{
+ int err, count;
+ struct cpufreq_frequency_table *table;
+
+ err = balanced_sysfs();
+ if (err)
+ return err;
+
+ balanced_wq = alloc_workqueue("cpuquiet-balanced",
+ WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1);
+ if (!balanced_wq)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&balanced_work, balanced_work_func);
+
+ up_delay = msecs_to_jiffies(1000);
+ down_delay = msecs_to_jiffies(2000);
+
+ table = cpufreq_frequency_get_table(0);
+ for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
+ ;
+
+ idle_top_freq = table[(count / 2) - 1].frequency;
+ idle_bottom_freq = table[(count / 2) - 2].frequency;
+
+ cpufreq_register_notifier(&balanced_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ init_timer(&load_timer);
+ load_timer.function = calculate_load_timer;
+
+ return 0;
+}
+
+struct cpuquiet_governor balanced_governor = {
+ .name = "balanced",
+ .start = balanced_start,
+ .stop = balanced_stop,
+ .owner = THIS_MODULE,
+};
+
+static int __init init_balanced(void)
+{
+ return cpuquiet_register_governor(&balanced_governor);
+}
+
+static void __exit exit_balanced(void)
+{
+ cpuquiet_unregister_governor(&balanced_governor);
+}
+
+MODULE_LICENSE("GPL");
+module_init(init_balanced);
+module_exit(exit_balanced);
+
diff --git a/drivers/cpuquiet/governors/userspace.c b/drivers/cpuquiet/governors/userspace.c
new file mode 100644
index 000000000000..470056c5e32a
--- /dev/null
+++ b/drivers/cpuquiet/governors/userspace.c
@@ -0,0 +1,56 @@
+/*
+ * 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/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+#include <linux/sysfs.h>
+
+static DEFINE_MUTEX(userspace_mutex);
+
+static int governor_set(unsigned int cpu, bool active)
+{
+ mutex_lock(&userspace_mutex);
+ if (active)
+ cpuquiet_wake_cpu(cpu);
+ else
+ cpuquiet_quiesence_cpu(cpu);
+ mutex_unlock(&userspace_mutex);
+
+ return 0;
+}
+
+struct cpuquiet_governor userspace_governor = {
+ .name = "userspace",
+ .store_active = governor_set,
+ .owner = THIS_MODULE,
+};
+
+static int __init init_usermode(void)
+{
+ return cpuquiet_register_governor(&userspace_governor);
+}
+
+static void __exit exit_usermode(void)
+{
+ cpuquiet_unregister_governor(&userspace_governor);
+}
+
+MODULE_LICENSE("GPL");
+module_init(init_usermode);
+module_exit(exit_usermode);
diff --git a/drivers/cpuquiet/sysfs.c b/drivers/cpuquiet/sysfs.c
new file mode 100644
index 000000000000..1e1c14865b24
--- /dev/null
+++ b/drivers/cpuquiet/sysfs.c
@@ -0,0 +1,290 @@
+/*
+ * 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/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/cpuquiet.h>
+
+#include "cpuquiet.h"
+
+struct cpuquiet_dev {
+ unsigned int cpu;
+ struct kobject kobj;
+};
+
+struct cpuquiet_sysfs_attr {
+ struct attribute attr;
+ ssize_t (*show)(char *);
+ ssize_t (*store)(const char *, size_t count);
+};
+
+static struct kobject *cpuquiet_global_kobject;
+struct cpuquiet_dev *cpuquiet_cpu_devices[CONFIG_NR_CPUS];
+
+static ssize_t show_current_governor(char *buf)
+{
+ ssize_t ret;
+
+ mutex_lock(&cpuquiet_lock);
+
+ if (cpuquiet_curr_governor)
+ ret = sprintf(buf, "%s\n", cpuquiet_curr_governor->name);
+ else
+ ret = sprintf(buf, "none\n");
+
+ mutex_unlock(&cpuquiet_lock);
+
+ return ret;
+
+}
+
+static ssize_t store_current_governor(const char *buf, size_t count)
+{
+ char name[CPUQUIET_NAME_LEN];
+ struct cpuquiet_governor *gov;
+ int len = count, ret = -EINVAL;
+
+ if (!len || len >= sizeof(name))
+ return -EINVAL;
+
+ memcpy(name, buf, count);
+ name[len] = '\0';
+ if (name[len - 1] == '\n')
+ name[--len] = '\0';
+
+ mutex_lock(&cpuquiet_lock);
+ gov = cpuquiet_find_governor(name);
+ mutex_unlock(&cpuquiet_lock);
+
+ if (gov)
+ ret = cpuquiet_switch_governor(gov);
+
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t available_governors_show(char *buf)
+{
+ ssize_t ret = 0, len;
+ struct cpuquiet_governor *gov;
+
+ mutex_lock(&cpuquiet_lock);
+ if (!list_empty(&cpuquiet_governors)) {
+ list_for_each_entry(gov, &cpuquiet_governors, governor_list) {
+ len = sprintf(buf, "%s ", gov->name);
+ buf += len;
+ ret += len;
+ }
+ buf--;
+ *buf = '\n';
+ } else
+ ret = sprintf(buf, "none\n");
+
+ mutex_unlock(&cpuquiet_lock);
+
+ return ret;
+}
+
+struct cpuquiet_sysfs_attr attr_current_governor = __ATTR(current_governor,
+ 0644, show_current_governor, store_current_governor);
+struct cpuquiet_sysfs_attr attr_governors = __ATTR_RO(available_governors);
+
+
+static struct attribute *cpuquiet_default_attrs[] = {
+ &attr_current_governor.attr,
+ &attr_governors.attr,
+ NULL
+};
+
+static ssize_t cpuquiet_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpuquiet_sysfs_attr *cattr =
+ container_of(attr, struct cpuquiet_sysfs_attr, attr);
+
+ return cattr->show(buf);
+}
+
+static ssize_t cpuquiet_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct cpuquiet_sysfs_attr *cattr =
+ container_of(attr, struct cpuquiet_sysfs_attr, attr);
+
+ if (cattr->store)
+ return cattr->store(buf, count);
+
+ return -EINVAL;
+}
+
+static const struct sysfs_ops cpuquiet_sysfs_ops = {
+ .show = cpuquiet_sysfs_show,
+ .store = cpuquiet_sysfs_store,
+};
+
+static struct kobj_type ktype_cpuquiet_sysfs = {
+ .sysfs_ops = &cpuquiet_sysfs_ops,
+ .default_attrs = cpuquiet_default_attrs,
+};
+
+int cpuquiet_add_group(struct attribute_group *attrs)
+{
+ return sysfs_create_group(cpuquiet_global_kobject, attrs);
+}
+
+void cpuquiet_remove_group(struct attribute_group *attrs)
+{
+ sysfs_remove_group(cpuquiet_global_kobject, attrs);
+}
+
+int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name)
+{
+ int err;
+
+ err = kobject_init_and_add(kobj, type, cpuquiet_global_kobject, name);
+ if (!err)
+ kobject_uevent(kobj, KOBJ_ADD);
+
+ return err;
+}
+
+int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
+ char *name, int cpu)
+{
+ int err;
+
+ err = kobject_init_and_add(kobj, type, &cpuquiet_cpu_devices[cpu]->kobj,
+ name);
+ if (!err)
+ kobject_uevent(kobj, KOBJ_ADD);
+
+ return err;
+}
+
+int cpuquiet_add_class_sysfs(struct sysdev_class *cls)
+{
+ int err;
+
+ cpuquiet_global_kobject = kzalloc(sizeof(*cpuquiet_global_kobject),
+ GFP_KERNEL);
+ if (!cpuquiet_global_kobject)
+ return -ENOMEM;
+
+ err = kobject_init_and_add(cpuquiet_global_kobject,
+ &ktype_cpuquiet_sysfs, &cls->kset.kobj, "cpuquiet");
+ if (!err)
+ kobject_uevent(cpuquiet_global_kobject, KOBJ_ADD);
+
+ return err;
+}
+
+
+struct cpuquiet_attr {
+ struct attribute attr;
+ ssize_t (*show)(unsigned int, char *);
+ ssize_t (*store)(unsigned int, const char *, size_t count);
+};
+
+
+static ssize_t cpuquiet_state_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpuquiet_attr *cattr = container_of(attr,
+ struct cpuquiet_attr, attr);
+ struct cpuquiet_dev *dev = container_of(kobj,
+ struct cpuquiet_dev, kobj);
+
+ return cattr->show(dev->cpu, buf);
+}
+
+static ssize_t cpuquiet_state_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct cpuquiet_attr *cattr = container_of(attr,
+ struct cpuquiet_attr, attr);
+ struct cpuquiet_dev *dev = container_of(kobj,
+ struct cpuquiet_dev, kobj);
+
+ if (cattr->store)
+ return cattr->store(dev->cpu, buf, count);
+
+ return -EINVAL;
+}
+
+static ssize_t show_active(unsigned int cpu, char *buf)
+{
+ return sprintf(buf, "%u\n", cpu_online(cpu));
+}
+
+static ssize_t store_active(unsigned int cpu, const char *value, size_t count)
+{
+ unsigned int active;
+ int ret;
+
+ if (!cpuquiet_curr_governor->store_active)
+ return -EINVAL;
+
+ ret = sscanf(value, "%u", &active);
+ if (ret != 1)
+ return -EINVAL;
+
+ cpuquiet_curr_governor->store_active(cpu, active);
+
+ return count;
+}
+
+struct cpuquiet_attr attr_active = __ATTR(active, 0644, show_active,
+ store_active);
+
+static struct attribute *cpuquiet_default_cpu_attrs[] = {
+ &attr_active.attr,
+ NULL
+};
+
+static const struct sysfs_ops cpuquiet_cpu_sysfs_ops = {
+ .show = cpuquiet_state_show,
+ .store = cpuquiet_state_store,
+};
+
+static struct kobj_type ktype_cpuquiet = {
+ .sysfs_ops = &cpuquiet_cpu_sysfs_ops,
+ .default_attrs = cpuquiet_default_cpu_attrs,
+};
+
+void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu)
+{
+ struct cpuquiet_dev *dev;
+ int err;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev->cpu = cpu;
+ cpuquiet_cpu_devices[cpu] = dev;
+ err = kobject_init_and_add(&dev->kobj, &ktype_cpuquiet,
+ &sys_dev->kobj, "cpuquiet");
+ if (!err)
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+}
+
+void cpuquiet_remove_dev(unsigned int cpu)
+{
+ kobject_put(cpuquiet_cpu_devices[cpu]);
+}
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index 3c1a0ce4638e..7d7285e06fb7 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -1,4 +1,6 @@
GCOV_PROFILE := y
+
+subdir-ccflags-y := -Werror
#
# Makefile for the video capture/playback device drivers.
#
diff --git a/drivers/media/video/tegra/ad5816.c b/drivers/media/video/tegra/ad5816.c
index ed113b3a187c..2005f150d6fd 100644
--- a/drivers/media/video/tegra/ad5816.c
+++ b/drivers/media/video/tegra/ad5816.c
@@ -628,16 +628,6 @@ static int ad5816_dev_id(struct ad5816_info *info)
return err;
}
-static void ad5816_sts_rd(struct ad5816_info *info)
-{
- /**
- * Device specific code for status
- *
- * TODO: Ad5816 has support to get status for over/under
- * voltage conditions but currently this feature is not
- * required.
- */
-}
/**
* Below are device specific functions.
*/
@@ -663,12 +653,13 @@ static int ad5816_position_rd(struct ad5816_info *info, unsigned *position)
static int ad5816_position_wr(struct ad5816_info *info, unsigned position)
{
- position = position + info->config.pos_low;
+ u16 data;
+ position = position + info->config.pos_low;
if(position > info->config.pos_high)
position = info->config.pos_high;
- u16 data = position & 0x03ff;
+ data = position & 0x03ff;
return ad5816_i2c_wr16(info, VCM_CODE_MSB, data);
}
@@ -757,10 +748,8 @@ static int ad5816_param_rd(struct ad5816_info *info, unsigned long arg)
}
static int ad5816_param_wr_s(struct ad5816_info *info,
- struct nvc_param *params,
- u32 u32val)
+ struct nvc_param *params, u32 u32val)
{
- struct nvc_focus_cap cap;
u8 u8val;
int err = 0;
u8val = (u8)u32val;
@@ -1102,10 +1091,11 @@ static int ad5816_probe(
struct i2c_client *client,
const struct i2c_device_id *id)
{
- pr_info("ad5816: probing focuser.\n");
struct ad5816_info *info;
char dname[16];
int err;
+
+ pr_info("ad5816: probing focuser.\n");
dev_dbg(&client->dev, "%s\n", __func__);
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c
index b695aa16af59..c593a0ad756b 100644
--- a/drivers/media/video/tegra/nvavp/nvavp_dev.c
+++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c
@@ -205,8 +205,8 @@ static void nvavp_set_channel_control_area(struct nvavp_info *nvavp, int channel
writel(0x0, &control->get);
pr_debug("nvavp_set_channel_control_area for channel_id (%d):\
- control->put (0x%x) control->get (0x%x)\n",
- channel_id, &control->put, &control->get);
+ control->put (0x%08x) control->get (0x%08x)\n",
+ channel_id, (u32) &control->put, (u32) &control->get);
/* enable avp VDE clock control and disable iram clock gating */
writel(0x0, &control->idle_clk_enable);
@@ -548,7 +548,7 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
control = channel_info->os_control;
pr_debug("nvavp_pushbuffer_update for channel_id (%d):\
control->put (0x%x) control->get (0x%x)\n",
- channel_id, &control->put, &control->get);
+ channel_id, (u32) &control->put, (u32) &control->get);
mutex_lock(&channel_info->pushbuffer_lock);
diff --git a/drivers/media/video/tegra/ov5640.c b/drivers/media/video/tegra/ov5640.c
index b20c036bb06c..26580b0105e3 100644
--- a/drivers/media/video/tegra/ov5640.c
+++ b/drivers/media/video/tegra/ov5640.c
@@ -108,6 +108,7 @@ static int ov5640_read_reg(struct i2c_client *client, u16 addr, u8 *val)
return 0;
}
+#ifdef KERNEL_WARNING
static int ov5640_write_reg(struct i2c_client *client, u8 addr, u8 value)
{
int count;
@@ -134,6 +135,7 @@ static int ov5640_write_reg(struct i2c_client *client, u8 addr, u8 value)
addr, (u32)value);
return -EIO;
}
+#endif
static int ov5640_write_bulk_reg(struct i2c_client *client, u8 *data, int len)
{
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c
index de0c662ba613..5c1cbfe63a28 100644
--- a/drivers/media/video/tegra/tegra_camera.c
+++ b/drivers/media/video/tegra/tegra_camera.c
@@ -183,10 +183,13 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- u32 val;
- void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
- val = readl(apb_misc + 0x42c);
- writel(val | 0x1, apb_misc + 0x42c);
+ {
+ u32 val;
+ void __iomem *apb_misc =
+ IO_ADDRESS(TEGRA_APB_MISC_BASE);
+ val = readl(apb_misc + 0x42c);
+ writel(val | 0x1, apb_misc + 0x42c);
+ }
#endif
}
diff --git a/drivers/misc/bcm4329_rfkill.c b/drivers/misc/bcm4329_rfkill.c
index cf56768e2baa..9dc33fd51e59 100644
--- a/drivers/misc/bcm4329_rfkill.c
+++ b/drivers/misc/bcm4329_rfkill.c
@@ -111,7 +111,9 @@ static int bcm4329_rfkill_probe(struct platform_device *pdev)
ret = gpio_request(bcm4329_rfkill->gpio_reset,
"bcm4329_nreset_gpio");
} else {
- pr_warn("%s : can't find reset gpio.\n", __func__);
+ pr_warn("%s : can't find reset gpio. "
+ "reset gpio may not be defined for "
+ "this platform \n", __func__);
bcm4329_rfkill->gpio_reset = 0;
}
@@ -123,7 +125,9 @@ static int bcm4329_rfkill_probe(struct platform_device *pdev)
ret = gpio_request(bcm4329_rfkill->gpio_shutdown,
"bcm4329_nshutdown_gpio");
} else {
- pr_warn("%s : can't find shutdown gpio.\n", __func__);
+ pr_warn("%s : can't find shutdown gpio "
+ "shutdown gpio may not be defined for "
+ "this platform \n", __func__);
bcm4329_rfkill->gpio_shutdown = 0;
}
diff --git a/drivers/tty/serial/tegra_hsuart.c b/drivers/tty/serial/tegra_hsuart.c
index 484e228dc044..ea20de6ebb41 100644
--- a/drivers/tty/serial/tegra_hsuart.c
+++ b/drivers/tty/serial/tegra_hsuart.c
@@ -895,7 +895,7 @@ static int tegra_startup(struct uart_port *u)
goto fail;
pdata = u->dev->platform_data;
- if (pdata->is_loopback)
+ if (pdata && pdata->is_loopback)
t->mcr_shadow |= UART_MCR_LOOP;
dev_dbg(u->dev, "Requesting IRQ %d\n", u->irq);
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
index 2719a62f873d..9aa155caaebd 100644
--- a/drivers/usb/otg/tegra-otg.c
+++ b/drivers/usb/otg/tegra-otg.c
@@ -66,6 +66,7 @@ struct tegra_otg_data {
callback_t charger_cb;
void *charger_cb_data;
bool interrupt_mode;
+ bool builtin_host;
};
static struct tegra_otg_data *tegra_clone;
@@ -104,8 +105,12 @@ static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en)
clk_enable(tegra->clk);
val = otg_readl(tegra, USB_PHY_WAKEUP);
- if (en)
- val |= USB_INT_EN;
+ if (en) {
+ if (tegra->builtin_host)
+ val |= USB_INT_EN;
+ else
+ val = USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN | USB_ID_PIN_WAKEUP_EN;
+ }
else
val &= ~USB_INT_EN;
otg_writel(tegra, val, USB_PHY_WAKEUP);
@@ -147,6 +152,7 @@ static void tegra_start_host(struct tegra_otg_data *tegra)
memcpy(platform_data, pdata->ehci_pdata,
sizeof(struct tegra_usb_platform_data));
pdev->dev.platform_data = platform_data;
+ tegra->builtin_host = !pdata->ehci_pdata->builtin_host_disabled;
val = platform_device_add(pdev);
if (val)
@@ -253,7 +259,7 @@ static void irq_work(struct work_struct *work)
DBG("%s(%d) got vbus interrupt\n", __func__, __LINE__);
}
- if (!(status & USB_ID_STATUS))
+ if (!(status & USB_ID_STATUS) && (status & USB_ID_INT_EN))
to = OTG_STATE_A_HOST;
else if (status & USB_VBUS_STATUS && from != OTG_STATE_A_HOST)
to = OTG_STATE_B_PERIPHERAL;
@@ -302,8 +308,12 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS))
val |= USB_VBUS_INT_STATUS;
- else if (!(val & USB_ID_STATUS))
- val |= USB_ID_INT_STATUS;
+ else if (!(val & USB_ID_STATUS)) {
+ if(!tegra->builtin_host)
+ val &= ~USB_ID_INT_STATUS;
+ else
+ val |= USB_ID_INT_STATUS;
+ }
else
val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS);
@@ -364,7 +374,6 @@ static ssize_t store_host_en(struct device *dev, struct device_attribute *attr,
struct platform_device *pdev = to_platform_device(dev);
struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
unsigned long host;
- int err;
if (sscanf(buf, "%d", &host) != 1 || host < 0 || host > 1)
return -EINVAL;
@@ -507,9 +516,9 @@ static int tegra_otg_suspend(struct device *dev)
tegra_state_name(otg->state));
clk_enable(tegra->clk);
- val = readl(tegra->regs + USB_PHY_WAKEUP);
- val &= ~USB_INT_EN;
- writel(val, tegra->regs + USB_PHY_WAKEUP);
+ val = otg_readl(tegra, USB_PHY_WAKEUP);
+ val &= ~(USB_ID_INT_EN | USB_VBUS_INT_EN);
+ otg_writel(tegra, val, USB_PHY_WAKEUP);
clk_disable(tegra->clk);
/* Suspend peripheral mode, host mode is taken care by host driver */
@@ -531,8 +540,8 @@ static void tegra_otg_resume(struct device *dev)
/* Clear pending interrupts */
clk_enable(tegra->clk);
- val = readl(tegra->regs + USB_PHY_WAKEUP);
- writel(val, tegra->regs + USB_PHY_WAKEUP);
+ val = otg_readl(tegra, USB_PHY_WAKEUP);
+ otg_writel(tegra, val, USB_PHY_WAKEUP);
DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val);
clk_disable(tegra->clk);
@@ -542,15 +551,16 @@ static void tegra_otg_resume(struct device *dev)
/* Enable interrupt and call work to set to appropriate state */
spin_lock_irqsave(&tegra->lock, flags);
- tegra->int_status = (val | USB_INT_EN);
+ if (tegra->builtin_host)
+ tegra->int_status = val | USB_INT_EN;
+ else
+ tegra->int_status = val | USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN |
+ USB_ID_PIN_WAKEUP_EN;
+
spin_unlock_irqrestore(&tegra->lock, flags);
irq_work(&tegra->work);
- clk_enable(tegra->clk);
- val = readl(tegra->regs + USB_PHY_WAKEUP);
- val |= USB_INT_EN;
- writel(val, tegra->regs + USB_PHY_WAKEUP);
- clk_disable(tegra->clk);
+ enable_interrupt(tegra, true);
DBG("%s(%d) END\n", __func__, __LINE__);
}
diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile
index ebe9e890fad6..8a826412f90c 100644
--- a/drivers/video/tegra/dc/Makefile
+++ b/drivers/video/tegra/dc/Makefile
@@ -1,4 +1,5 @@
GCOV_PROFILE := y
+obj-y += bandwidth.o
obj-y += dc.o
obj-y += rgb.o
obj-y += hdmi.o
diff --git a/drivers/video/tegra/dc/bandwidth.c b/drivers/video/tegra/dc/bandwidth.c
new file mode 100644
index 000000000000..a1da7ef0a995
--- /dev/null
+++ b/drivers/video/tegra/dc/bandwidth.c
@@ -0,0 +1,275 @@
+/*
+ * drivers/video/tegra/dc/bandwidth.c
+ *
+ * Copyright (C) 2010-2012 NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <mach/clk.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+#include <mach/mc.h>
+#include <linux/nvhost.h>
+#include <mach/latency_allowance.h>
+
+#include "dc_reg.h"
+#include "dc_config.h"
+#include "dc_priv.h"
+
+static int use_dynamic_emc = 1;
+
+module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR);
+
+/* uses the larger of w->bandwidth or w->new_bandwidth, and copies
+ * w->new_bandwidth into w->bandwidth */
+static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
+ struct tegra_dc_win *w)
+{
+ /* windows A, B, C for first and second display */
+ static const enum tegra_la_id la_id_tab[2][3] = {
+ /* first display */
+ { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B,
+ TEGRA_LA_DISPLAY_0C },
+ /* second display */
+ { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB,
+ TEGRA_LA_DISPLAY_0CB },
+ };
+ /* window B V-filter tap for first and second display. */
+ static const enum tegra_la_id vfilter_tab[2] = {
+ TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB,
+ };
+ unsigned long bw;
+
+ BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab));
+ BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab));
+ BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab));
+
+ bw = max(w->bandwidth, w->new_bandwidth);
+
+ /* tegra_dc_get_bandwidth() treats V filter windows as double
+ * bandwidth, but LA has a seperate client for V filter */
+ if (w->idx == 1 && win_use_v_filter(dc, w))
+ bw /= 2;
+
+ /* our bandwidth is in kbytes/sec, but LA takes MBps.
+ * round up bandwidth to next 1MBps */
+ bw = bw / 1000 + 1;
+
+#ifdef CONFIG_TEGRA_SILICON_PLATFORM
+ tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw);
+ /* if window B, also set the 1B client for the 2-tap V filter. */
+ if (w->idx == 1)
+ tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw);
+#endif
+
+ w->bandwidth = w->new_bandwidth;
+}
+
+static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a,
+ struct tegra_dc_win *b)
+{
+ if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b))
+ return 0;
+
+ /* because memory access to load the fifo can overlap, only care
+ * if windows overlap vertically */
+ return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) ||
+ ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y));
+}
+
+static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[],
+ int n)
+{
+ unsigned i;
+ unsigned j;
+ unsigned overlap_count;
+ unsigned max_bw = 0;
+
+ WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely"
+ "inaccurate.\n");
+
+ /* If we had a large number of windows, we would compute adjacency
+ * graph representing 2 window overlaps, find all cliques in the graph,
+ * assign bandwidth to each clique, and then select the clique with
+ * maximum bandwidth. But because we have at most 3 windows,
+ * implementing proper Bron-Kerbosh algorithm would be an overkill,
+ * brute force will suffice.
+ *
+ * Thus: find maximum bandwidth for either single or a pair of windows
+ * and count number of window pair overlaps. If there are three
+ * pairs, all 3 window overlap.
+ */
+
+ overlap_count = 0;
+ for (i = 0; i < n; i++) {
+ unsigned int bw1;
+
+ if (wins[i] == NULL)
+ continue;
+ bw1 = wins[i]->new_bandwidth;
+ if (bw1 > max_bw)
+ /* Single window */
+ max_bw = bw1;
+
+ for (j = i + 1; j < n; j++) {
+ if (wins[j] == NULL)
+ continue;
+ if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) {
+ unsigned int bw2 = wins[j]->new_bandwidth;
+ if (bw1 + bw2 > max_bw)
+ /* Window pair overlaps */
+ max_bw = bw1 + bw2;
+ overlap_count++;
+ }
+ }
+ }
+
+ if (overlap_count == 3)
+ /* All three windows overlap */
+ max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth +
+ wins[2]->new_bandwidth;
+
+ return max_bw;
+}
+
+/*
+ * Calculate peak EMC bandwidth for each enabled window =
+ * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
+ * (windows_tiling ? 2 : 1)
+ *
+ * note:
+ * (*) We use 2 tap V filter, so need double BW if use V filter
+ * (*) Tiling mode on T30 and DDR3 requires double BW
+ *
+ * return:
+ * bandwidth in kBps
+ */
+static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
+ struct tegra_dc_win *w)
+{
+ unsigned long ret;
+ int tiled_windows_bw_multiplier;
+ unsigned long bpp;
+
+ if (!WIN_IS_ENABLED(w))
+ return 0;
+
+ if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 ||
+ w->out_w == 0 || w->out_h == 0)
+ return 0;
+
+ tiled_windows_bw_multiplier =
+ tegra_mc_get_tiled_memory_bandwidth_multiplier();
+
+ /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel,
+ * but the size reported by tegra_dc_fmt_bpp for the planar version
+ * is of the luma plane's size only. */
+ bpp = tegra_dc_is_yuv_planar(w->fmt) ?
+ 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
+ ret = dc->mode.pclk / 1000UL * bpp / 8 * (
+ win_use_v_filter(dc, w) ? 2 : 1) *
+ dfixed_trunc(w->w) / w->out_w * (WIN_IS_TILED(w) ?
+ tiled_windows_bw_multiplier : 1);
+
+ return ret;
+}
+
+static unsigned long tegra_dc_get_bandwidth(
+ struct tegra_dc_win *windows[], int n)
+{
+ int i;
+
+ BUG_ON(n > DC_N_WINDOWS);
+
+ /* emc rate and latency allowance both need to know per window
+ * bandwidths */
+ for (i = 0; i < n; i++) {
+ struct tegra_dc_win *w = windows[i];
+
+ if (w)
+ w->new_bandwidth =
+ tegra_dc_calc_win_bandwidth(w->dc, w);
+ }
+
+ return tegra_dc_find_max_bandwidth(windows, n);
+}
+
+/* to save power, call when display memory clients would be idle */
+void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
+{
+ trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__,
+ dc->emc_clk_rate);
+ if (tegra_is_clk_enabled(dc->emc_clk))
+ clk_disable(dc->emc_clk);
+ dc->emc_clk_rate = 0;
+}
+
+/* use the larger of dc->emc_clk_rate or dc->new_emc_clk_rate, and copies
+ * dc->new_emc_clk_rate into dc->emc_clk_rate.
+ * calling this function both before and after a flip is sufficient to select
+ * the best possible frequency and latency allowance.
+ */
+void tegra_dc_program_bandwidth(struct tegra_dc *dc)
+{
+ unsigned i;
+
+ if (dc->emc_clk_rate != dc->new_emc_clk_rate) {
+ /* going from 0 to non-zero */
+ if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk))
+ clk_enable(dc->emc_clk);
+
+ clk_set_rate(dc->emc_clk,
+ max(dc->emc_clk_rate, dc->new_emc_clk_rate));
+ dc->emc_clk_rate = dc->new_emc_clk_rate;
+
+ if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
+ clk_disable(dc->emc_clk);
+ }
+
+ for (i = 0; i < DC_N_WINDOWS; i++) {
+ struct tegra_dc_win *w = &dc->windows[i];
+
+ if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0)
+ tegra_dc_set_latency_allowance(dc, w);
+ trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx,
+ w->bandwidth);
+ }
+}
+
+int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
+{
+ unsigned long new_rate;
+ struct tegra_dc *dc;
+
+ if (!use_dynamic_emc)
+ return 0;
+
+ dc = windows[0]->dc;
+
+ /* calculate the new rate based on this POST */
+ new_rate = tegra_dc_get_bandwidth(windows, n);
+ if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n"))
+ new_rate = ULONG_MAX;
+ else
+ new_rate = EMC_BW_TO_FREQ(new_rate * 1000);
+
+ if (tegra_dc_has_multiple_dc())
+ new_rate = ULONG_MAX;
+
+ trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate);
+ dc->new_emc_clk_rate = new_rate;
+
+ return 0;
+}
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 5c960f9e45c0..f4844f1eecfc 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -85,106 +85,11 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc);
module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
-static int use_dynamic_emc = 1;
-
-module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR);
-
struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
DEFINE_MUTEX(tegra_dc_lock);
DEFINE_MUTEX(shared_lock);
-static inline bool win_use_v_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
-{
- return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) &&
- win->h.full != dfixed_const(win->out_h);
-}
-static inline bool win_use_h_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
-{
- return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) &&
- win->w.full != dfixed_const(win->out_w);
-}
-
-static inline int tegra_dc_fmt_bpp(int fmt)
-{
- switch (fmt) {
- case TEGRA_WIN_FMT_P1:
- return 1;
-
- case TEGRA_WIN_FMT_P2:
- return 2;
-
- case TEGRA_WIN_FMT_P4:
- return 4;
-
- case TEGRA_WIN_FMT_P8:
- return 8;
-
- case TEGRA_WIN_FMT_B4G4R4A4:
- case TEGRA_WIN_FMT_B5G5R5A:
- case TEGRA_WIN_FMT_B5G6R5:
- case TEGRA_WIN_FMT_AB5G5R5:
- return 16;
-
- case TEGRA_WIN_FMT_B8G8R8A8:
- case TEGRA_WIN_FMT_R8G8B8A8:
- case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
- case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
- return 32;
-
- /* for planar formats, size of the Y plane, 8bit */
- case TEGRA_WIN_FMT_YCbCr420P:
- case TEGRA_WIN_FMT_YUV420P:
- case TEGRA_WIN_FMT_YCbCr422P:
- case TEGRA_WIN_FMT_YUV422P:
- case TEGRA_WIN_FMT_YCbCr422R:
- case TEGRA_WIN_FMT_YUV422R:
- case TEGRA_WIN_FMT_YCbCr422RA:
- case TEGRA_WIN_FMT_YUV422RA:
- return 8;
-
- /* YUYV packed into 32-bits */
- case TEGRA_WIN_FMT_YCbCr422:
- case TEGRA_WIN_FMT_YUV422:
- return 16;
- }
- return 0;
-}
-
-static inline bool tegra_dc_is_yuv(int fmt)
-{
- switch (fmt) {
- case TEGRA_WIN_FMT_YUV420P:
- case TEGRA_WIN_FMT_YCbCr420P:
- case TEGRA_WIN_FMT_YCbCr422P:
- case TEGRA_WIN_FMT_YUV422P:
- case TEGRA_WIN_FMT_YCbCr422:
- case TEGRA_WIN_FMT_YUV422:
- case TEGRA_WIN_FMT_YCbCr422R:
- case TEGRA_WIN_FMT_YUV422R:
- case TEGRA_WIN_FMT_YCbCr422RA:
- case TEGRA_WIN_FMT_YUV422RA:
- return true;
- }
- return false;
-}
-
-static inline bool tegra_dc_is_yuv_planar(int fmt)
-{
- switch (fmt) {
- case TEGRA_WIN_FMT_YUV420P:
- case TEGRA_WIN_FMT_YCbCr420P:
- case TEGRA_WIN_FMT_YCbCr422P:
- case TEGRA_WIN_FMT_YUV422P:
- case TEGRA_WIN_FMT_YCbCr422R:
- case TEGRA_WIN_FMT_YUV422R:
- case TEGRA_WIN_FMT_YCbCr422RA:
- case TEGRA_WIN_FMT_YUV422RA:
- return true;
- }
- return false;
-}
-
#define DUMP_REG(a) do { \
snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
#a, a, tegra_dc_readl(dc, a)); \
@@ -569,7 +474,7 @@ out:
return ret;
}
-static unsigned int tegra_dc_has_multiple_dc(void)
+unsigned int tegra_dc_has_multiple_dc(void)
{
unsigned int idx;
unsigned int cnt = 0;
@@ -587,7 +492,6 @@ static unsigned int tegra_dc_has_multiple_dc(void)
* return: stride size in bytes for window win. or 0 if unavailble. */
int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
{
- u32 tmp;
u32 stride;
if (!dc->enabled)
@@ -595,8 +499,8 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
BUG_ON(win > DC_N_WINDOWS);
tegra_dc_writel(dc, WINDOW_A_SELECT << win,
DC_CMD_DISPLAY_WINDOW_HEADER);
- tmp = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
- return GET_LINE_STRIDE(tmp);
+ stride = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
+ return GET_LINE_STRIDE(stride);
}
EXPORT_SYMBOL(tegra_dc_get_stride);
@@ -883,239 +787,6 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
}
}
-static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
- struct tegra_dc_win *w)
-{
- /* windows A, B, C for first and second display */
- static const enum tegra_la_id la_id_tab[2][3] = {
- /* first display */
- { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B,
- TEGRA_LA_DISPLAY_0C },
- /* second display */
- { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB,
- TEGRA_LA_DISPLAY_0CB },
- };
- /* window B V-filter tap for first and second display. */
- static const enum tegra_la_id vfilter_tab[2] = {
- TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB,
- };
- unsigned long bw;
-
- BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab));
- BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab));
- BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab));
-
- bw = w->new_bandwidth;
-
- /* tegra_dc_get_bandwidth() treats V filter windows as double
- * bandwidth, but LA has a seperate client for V filter */
- if (w->idx == 1 && win_use_v_filter(dc, w))
- bw /= 2;
-
- /* our bandwidth is in kbytes/sec, but LA takes MBps.
- * round up bandwidth to next 1MBps */
- bw = bw / 1000 + 1;
-
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
- tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw);
- /* if window B, also set the 1B client for the 2-tap V filter. */
- if (w->idx == 1)
- tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw);
-#endif
-
- w->bandwidth = w->new_bandwidth;
-}
-
-static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a,
- struct tegra_dc_win *b)
-{
- if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b))
- return 0;
-
- /* because memory access to load the fifo can overlap, only care
- * if windows overlap vertically */
- return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) ||
- ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y));
-}
-
-static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[],
- int n)
-{
- unsigned i;
- unsigned j;
- unsigned overlap_count;
- unsigned max_bw = 0;
-
- WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely"
- "inaccurate.\n");
-
- /* If we had a large number of windows, we would compute adjacency
- * graph representing 2 window overlaps, find all cliques in the graph,
- * assign bandwidth to each clique, and then select the clique with
- * maximum bandwidth. But because we have at most 3 windows,
- * implementing proper Bron-Kerbosh algorithm would be an overkill,
- * brute force will suffice.
- *
- * Thus: find maximum bandwidth for either single or a pair of windows
- * and count number of window pair overlaps. If there are three
- * pairs, all 3 window overlap.
- */
-
- overlap_count = 0;
- for (i = 0; i < n; i++) {
- unsigned int bw1;
-
- if (wins[i] == NULL)
- continue;
- bw1 = wins[i]->new_bandwidth;
- if (bw1 > max_bw)
- /* Single window */
- max_bw = bw1;
-
- for (j = i + 1; j < n; j++) {
- if (wins[j] == NULL)
- continue;
- if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) {
- unsigned int bw2 = wins[j]->new_bandwidth;
- if (bw1 + bw2 > max_bw)
- /* Window pair overlaps */
- max_bw = bw1 + bw2;
- overlap_count++;
- }
- }
- }
-
- if (overlap_count == 3)
- /* All three windows overlap */
- max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth +
- wins[2]->new_bandwidth;
-
- return max_bw;
-}
-
-/*
- * Calculate peak EMC bandwidth for each enabled window =
- * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
- * (windows_tiling ? 2 : 1)
- *
- * note:
- * (*) We use 2 tap V filter, so need double BW if use V filter
- * (*) Tiling mode on T30 and DDR3 requires double BW
- *
- * return:
- * bandwidth in kBps
- */
-static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
- struct tegra_dc_win *w)
-{
- unsigned long ret;
- int tiled_windows_bw_multiplier;
- unsigned long bpp;
-
- if (!WIN_IS_ENABLED(w))
- return 0;
-
- if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 ||
- w->out_w == 0 || w->out_h == 0)
- return 0;
-
- tiled_windows_bw_multiplier =
- tegra_mc_get_tiled_memory_bandwidth_multiplier();
-
- /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel,
- * but the size reported by tegra_dc_fmt_bpp for the planar version
- * is of the luma plane's size only. */
- bpp = tegra_dc_is_yuv_planar(w->fmt) ?
- 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
- ret = dc->mode.pclk / 1000UL * bpp / 8 * (win_use_v_filter(dc, w) ? 2 : 1)
- * dfixed_trunc(w->w) / w->out_w *
- (WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1);
-
- return ret;
-}
-
-static unsigned long tegra_dc_get_bandwidth(
- struct tegra_dc_win *windows[], int n)
-{
- int i;
-
- BUG_ON(n > DC_N_WINDOWS);
-
- /* emc rate and latency allowance both need to know per window
- * bandwidths */
- for (i = 0; i < n; i++) {
- struct tegra_dc_win *w = windows[i];
-
- if (w)
- w->new_bandwidth =
- tegra_dc_calc_win_bandwidth(w->dc, w);
- }
-
- return tegra_dc_find_max_bandwidth(windows, n);
-}
-
-/* to save power, call when display memory clients would be idle */
-static void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
-{
- trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__,
- dc->emc_clk_rate);
- if (tegra_is_clk_enabled(dc->emc_clk))
- clk_disable(dc->emc_clk);
- dc->emc_clk_rate = 0;
-}
-
-static void tegra_dc_program_bandwidth(struct tegra_dc *dc)
-{
- unsigned i;
-
- if (dc->emc_clk_rate != dc->new_emc_clk_rate) {
- /* going from 0 to non-zero */
- if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk))
- clk_enable(dc->emc_clk);
-
- dc->emc_clk_rate = dc->new_emc_clk_rate;
- clk_set_rate(dc->emc_clk, dc->emc_clk_rate);
-
- if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
- clk_disable(dc->emc_clk);
- }
-
- for (i = 0; i < DC_N_WINDOWS; i++) {
- struct tegra_dc_win *w = &dc->windows[i];
-
- if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0)
- tegra_dc_set_latency_allowance(dc, w);
- trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx,
- w->bandwidth);
- }
-}
-
-static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
-{
- unsigned long new_rate;
- struct tegra_dc *dc;
-
- if (!use_dynamic_emc)
- return 0;
-
- dc = windows[0]->dc;
-
- /* calculate the new rate based on this POST */
- new_rate = tegra_dc_get_bandwidth(windows, n);
- if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n"))
- new_rate = ULONG_MAX;
- else
- new_rate = EMC_BW_TO_FREQ(new_rate * 1000);
-
- if (tegra_dc_has_multiple_dc())
- new_rate = ULONG_MAX;
-
- trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate);
- dc->new_emc_clk_rate = new_rate;
-
- return 0;
-}
-
static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int,
bool v, unsigned Bpp)
{
@@ -2128,6 +1799,10 @@ static void tegra_dc_vblank(struct work_struct *work)
bool nvsd_updated = false;
mutex_lock(&dc->lock);
+ /* use the new frame's bandwidth setting instead of max(current, new),
+ * skip this if we're using tegra_dc_one_shot_worker() */
+ if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE))
+ tegra_dc_program_bandwidth(dc);
/* Update the SD brightness */
if (dc->enabled && dc->out->sd_settings)
@@ -3226,6 +2901,17 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
#endif /* CONFIG_PM */
+static void tegra_dc_shutdown(struct nvhost_device *ndev)
+{
+ struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+
+ if (!dc || !dc->enabled)
+ return;
+
+ tegra_dc_blank(dc);
+ tegra_dc_disable(dc);
+}
+
extern int suspend_set(const char *val, struct kernel_param *kp)
{
if (!strcmp(val, "dump"))
@@ -3260,6 +2946,7 @@ struct nvhost_driver tegra_dc_driver = {
.suspend = tegra_dc_suspend,
.resume = tegra_dc_resume,
#endif
+ .shutdown = tegra_dc_shutdown,
};
static int __init tegra_dc_module_init(void)
diff --git a/drivers/video/tegra/dc/dc_config.h b/drivers/video/tegra/dc/dc_config.h
index 314cd11e77f9..4cc924184275 100644
--- a/drivers/video/tegra/dc/dc_config.h
+++ b/drivers/video/tegra/dc/dc_config.h
@@ -145,4 +145,18 @@ int tegra_dc_feature_has_filter(struct tegra_dc *dc, int win_idx, int operation)
long *tegra_dc_parse_feature(struct tegra_dc *dc, int win_idx, int operation);
void tegra_dc_feature_register(struct tegra_dc *dc);
+
+static inline bool win_use_v_filter(struct tegra_dc *dc,
+ const struct tegra_dc_win *win)
+{
+ return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) &&
+ win->h.full != dfixed_const(win->out_h);
+}
+static inline bool win_use_h_filter(struct tegra_dc *dc,
+ const struct tegra_dc_win *win)
+{
+ return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) &&
+ win->w.full != dfixed_const(win->out_w);
+}
+
#endif
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index ce7ac3c7b0d0..4f53f60f2599 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -205,6 +205,86 @@ static inline unsigned long tegra_dc_get_default_emc_clk_rate(
return dc->pdata->emc_clk_rate ? dc->pdata->emc_clk_rate : ULONG_MAX;
}
+static inline int tegra_dc_fmt_bpp(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_P1:
+ return 1;
+
+ case TEGRA_WIN_FMT_P2:
+ return 2;
+
+ case TEGRA_WIN_FMT_P4:
+ return 4;
+
+ case TEGRA_WIN_FMT_P8:
+ return 8;
+
+ case TEGRA_WIN_FMT_B4G4R4A4:
+ case TEGRA_WIN_FMT_B5G5R5A:
+ case TEGRA_WIN_FMT_B5G6R5:
+ case TEGRA_WIN_FMT_AB5G5R5:
+ return 16;
+
+ case TEGRA_WIN_FMT_B8G8R8A8:
+ case TEGRA_WIN_FMT_R8G8B8A8:
+ case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
+ case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
+ return 32;
+
+ /* for planar formats, size of the Y plane, 8bit */
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
+ return 8;
+
+ /* YUYV packed into 32-bits */
+ case TEGRA_WIN_FMT_YCbCr422:
+ case TEGRA_WIN_FMT_YUV422:
+ return 16;
+ }
+ return 0;
+}
+
+static inline bool tegra_dc_is_yuv(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422:
+ case TEGRA_WIN_FMT_YUV422:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
+ return true;
+ }
+ return false;
+}
+
+static inline bool tegra_dc_is_yuv_planar(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
+ return true;
+ }
+ return false;
+}
+
void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk);
extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
@@ -227,5 +307,11 @@ 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);
-#endif
+/* defined in dc.c, used in bandwidth.c */
+unsigned int tegra_dc_has_multiple_dc(void);
+/* defined in bandwidth.c, used in dc.c */
+void tegra_dc_clear_bandwidth(struct tegra_dc *dc);
+void tegra_dc_program_bandwidth(struct tegra_dc *dc);
+int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n);
+#endif
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
index 59b9afcec0e4..3cd8de5085cd 100644
--- a/drivers/video/tegra/dc/hdmi.c
+++ b/drivers/video/tegra/dc/hdmi.c
@@ -1213,22 +1213,6 @@ static int tegra_dc_calc_clock_per_frame(const struct fb_videomode *mode)
(mode->upper_margin + mode->yres +
mode->lower_margin + mode->vsync_len);
}
-static bool tegra_dc_hdmi_mode_equal(const struct fb_videomode *mode1,
- const struct fb_videomode *mode2)
-{
- int clock_per_frame1 = tegra_dc_calc_clock_per_frame(mode1);
- int clock_per_frame2 = tegra_dc_calc_clock_per_frame(mode2);
-
- /* allows up to 1Hz of pixclock difference */
- return (clock_per_frame1 == clock_per_frame2 &&
- mode1->xres == mode2->xres &&
- mode1->yres == mode2->yres &&
- mode1->vmode == mode2->vmode &&
- (mode1->pixclock == mode2->pixclock ||
- (abs(PICOS2KHZ(mode1->pixclock) -
- PICOS2KHZ(mode2->pixclock)) *
- 1000 / clock_per_frame1 <= 1)));
-}
static bool tegra_dc_hdmi_valid_pixclock(const struct tegra_dc *dc,
const struct fb_videomode *mode)