diff --git a/arch/arm/configs/tegra3_android_defconfig b/arch/arm/configs/tegra3_android_defconfig index e641c54a3b7..0a7de658120 100644 --- a/arch/arm/configs/tegra3_android_defconfig +++ b/arch/arm/configs/tegra3_android_defconfig @@ -516,6 +516,8 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_GOV_INTELLIACTIVE=y +CONFIG_CPU_INPUT_BOOST=y + # # ARM CPU frequency scaling drivers diff --git a/arch/arm/mach-tegra/board-grouper-panel.c b/arch/arm/mach-tegra/board-grouper-panel.c index eb53370ec85..e19aa97ea01 100644 --- a/arch/arm/mach-tegra/board-grouper-panel.c +++ b/arch/arm/mach-tegra/board-grouper-panel.c @@ -217,6 +217,13 @@ static struct platform_device grouper_backlight_device = { }, }; +static int grouper_panel_prepoweroff(void) +{ + gpio_set_value(grouper_lvds_shutdown, 0); + + return 0; +} + static int grouper_panel_postpoweron(void) { if (grouper_lvds_reg == NULL) { @@ -600,6 +607,7 @@ static struct tegra_dc_out grouper_disp1_out = { .modes = grouper_panel_modes, .n_modes = ARRAY_SIZE(grouper_panel_modes), + .prepoweroff = grouper_panel_prepoweroff, .enable = grouper_panel_enable, .disable = grouper_panel_disable, .postpoweron = grouper_panel_postpoweron, diff --git a/arch/arm/mach-tegra/board-grouper.c b/arch/arm/mach-tegra/board-grouper.c index c525ab624f1..4a9654b21ba 100644 --- a/arch/arm/mach-tegra/board-grouper.c +++ b/arch/arm/mach-tegra/board-grouper.c @@ -1084,7 +1084,6 @@ static void __init tegra_grouper_init(void) grouper_misc_init(); tegra_thermal_init(&thermal_data); tegra_clk_init_from_table(grouper_clk_init_table); - grouper_pinmux_init(); grouper_misc_reset(); grouper_booting_info(); grouper_i2c_init(); @@ -1140,11 +1139,17 @@ static void __init tegra_grouper_reserve(void) grouper_ramconsole_reserve(SZ_1M); } +static void __init tegra_grouper_init_early(void) +{ + tegra_init_early(); + grouper_pinmux_init(); +} + MACHINE_START(GROUPER, "grouper") .boot_params = 0x80000100, .map_io = tegra_map_common_io, .reserve = tegra_grouper_reserve, - .init_early = tegra_init_early, + .init_early = tegra_grouper_init_early, .init_irq = tegra_init_irq, .timer = &tegra_timer, .init_machine = tegra_grouper_init, diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h index e19344dac15..006a85469e3 100644 --- a/arch/arm/mach-tegra/include/mach/dc.h +++ b/arch/arm/mach-tegra/include/mach/dc.h @@ -383,6 +383,7 @@ struct tegra_dc_out { u8 *out_sel_configs; unsigned n_out_sel_configs; + int (*prepoweroff)(void); int (*enable)(void); int (*postpoweron)(void); int (*disable)(void); diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index bc7938690ca..c49ee44cd4a 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -228,6 +228,11 @@ depends on X86 source "drivers/cpufreq/Kconfig.x86" endmenu +config CPU_INPUT_BOOST + bool "CPU Input Boost" + help + Boost the CPU on touchscreen, touchpad, and keypad input. + menu "ARM CPU frequency scaling drivers" depends on ARM source "drivers/cpufreq/Kconfig.arm" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index a6ad8c43449..02764c9bbdc 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -3,6 +3,9 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o +# CPU Input Boost +obj-$(CONFIG_CPU_INPUT_BOOST) += cpu_input_boost.o + # CPUfreq governors obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o diff --git a/drivers/cpufreq/cpu_input_boost.c b/drivers/cpufreq/cpu_input_boost.c new file mode 100644 index 00000000000..4fdf94d6285 --- /dev/null +++ b/drivers/cpufreq/cpu_input_boost.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2014-2015, Sultanxda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "CPU-boost: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum boost_status { + UNBOOST, + BOOST, +}; + +enum boost_pwr { + LOW, + MID, + HIGH, +}; + +struct boost_policy { + enum boost_status boost_state; +}; + +static DEFINE_PER_CPU(struct boost_policy, boost_info); +static struct workqueue_struct *boost_wq; +static struct work_struct boost_work; +static struct delayed_work restore_work; + +static bool boost_running; +static bool suspended; + +static u64 last_input_time; +#define MIN_INPUT_INTERVAL (150 * USEC_PER_MSEC) + +#define NUM_CPUS CONFIG_NR_CPUS + +/** + * Auto boost freq calculation: + * Requested boost freqs = maxfreq * boost_factor[i] / BOOST_FACTOR_DIVISOR, + * so the lowest boost freq in this case would be maxfreq * 3 / 7 + */ +static unsigned int boost_freq[3]; +static unsigned int boost_factor[3] = {3, 4, 5}; +#define BOOST_FACTOR_DIVISOR 7 + +/* Boost-freq level to use (high, mid, low) */ +static enum boost_pwr boost_level; + +/* Boost duration in millsecs */ +static unsigned int boost_ms; + +/* On/off switch */ +static unsigned int enabled; +module_param(enabled, uint, 0644); + +/** + * Percentage threshold used to boost CPUs (default 30%). A higher + * value will cause more CPUs to be boosted -- CPUs are boosted + * when ((current_freq/max_freq) * 100) < up_threshold + */ +static unsigned int up_threshold = 30; +module_param(up_threshold, uint, 0644); + +static void cpu_unboost_all(void) +{ + struct boost_policy *b; + unsigned int cpu; + + get_online_cpus(); + for_each_possible_cpu(cpu) { + b = &per_cpu(boost_info, cpu); + if (b->boost_state == BOOST) { + b->boost_state = UNBOOST; + if (cpu_online(cpu)) + cpufreq_update_policy(cpu); + } + } + put_online_cpus(); + boost_running = false; +} + +static void __cpuinit cpu_boost_main(struct work_struct *work) +{ + struct boost_policy *b; + struct cpufreq_policy *policy; + unsigned int cpu, num_cpus_boosted = 0, num_cpus_to_boost = 0; + + /* Num of CPUs to be boosted based on current freq of each online CPU */ + get_online_cpus(); + for_each_online_cpu(cpu) { + policy = cpufreq_cpu_get(cpu); + if (policy != NULL) { + if ((policy->cur * 100 / policy->max) < up_threshold) + num_cpus_to_boost++; + cpufreq_cpu_put(policy); + /* Only allow 2 CPUs to be staged for boosting from here */ + if (num_cpus_to_boost == 2) + break; + } + } + + /* Num of CPUs to be boosted based on how many of them are online */ + switch (num_online_cpus() * 100 / NUM_CPUS) { + case 25: + num_cpus_to_boost += 2; + break; + case 50 ... 75: + num_cpus_to_boost++; + break; + } + + /* Nothing to boost */ + if (!num_cpus_to_boost) { + put_online_cpus(); + boost_running = false; + return; + } + + /* Boost freq to use based on how many CPUs to boost */ + switch (num_cpus_to_boost * 100 / NUM_CPUS) { + case 25: + boost_level = HIGH; + break; + case 50: + boost_level = MID; + break; + default: + boost_level = LOW; + } + + /* Dual-core systems need more power */ + if (NUM_CPUS == 2) + boost_level++; + + /* Calculate boost duration */ + boost_ms = 3000 - ((num_cpus_to_boost * 750) + ((boost_level + 1) * 250)); + + /* Prioritize boosting of online CPUs */ + for_each_online_cpu(cpu) { + b = &per_cpu(boost_info, cpu); + b->boost_state = BOOST; + cpufreq_update_policy(cpu); + num_cpus_boosted++; + if (num_cpus_boosted == num_cpus_to_boost) + goto finish_boost; + } + + /* Boost offline CPUs if we still need to boost more CPUs */ + for_each_possible_cpu(cpu) { + b = &per_cpu(boost_info, cpu); + if (b->boost_state == UNBOOST) { + b->boost_state = BOOST; + num_cpus_boosted++; + if (num_cpus_boosted == num_cpus_to_boost) + goto finish_boost; + } + } + +finish_boost: + put_online_cpus(); + queue_delayed_work(boost_wq, &restore_work, + msecs_to_jiffies(boost_ms)); +} + +static void __cpuinit cpu_restore_main(struct work_struct *work) +{ + cpu_unboost_all(); +} + +static int cpu_do_boost(struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_policy *policy = data; + struct boost_policy *b = &per_cpu(boost_info, policy->cpu); + + if (val != CPUFREQ_ADJUST) + return NOTIFY_OK; + + switch (b->boost_state) { + case UNBOOST: + policy->min = policy->cpuinfo.min_freq; + break; + case BOOST: + if (boost_freq[boost_level] > policy->max) + policy->min = policy->max; + else + policy->min = boost_freq[boost_level]; + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block cpu_do_boost_nb = { + .notifier_call = cpu_do_boost, +}; + +static void cpu_boost_early_suspend(struct early_suspend *handler) +{ + suspended = true; + + if (cancel_delayed_work_sync(&restore_work)) + cpu_unboost_all(); +} + +static void __cpuinit cpu_boost_late_resume(struct early_suspend *handler) +{ + suspended = false; +} + +static struct early_suspend __refdata cpu_boost_early_suspend_handler = { + .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN, + .suspend = cpu_boost_early_suspend, + .resume = cpu_boost_late_resume, +}; + +static void cpu_boost_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + u64 now; + + if (boost_running) + return; + if (!enabled) + return; + if (suspended) + return; + + now = ktime_to_us(ktime_get()); + if (now - last_input_time < MIN_INPUT_INTERVAL) + return; + + boost_running = true; + queue_work(boost_wq, &boost_work); + last_input_time = ktime_to_us(ktime_get()); +} + +static int cpu_boost_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpu_input_boost"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void cpu_boost_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id cpu_boost_ids[] = { + /* multi-touch touchscreen */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, + /* touchpad */ + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, + /* keypad */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; + +static struct input_handler cpu_boost_input_handler = { + .event = cpu_boost_input_event, + .connect = cpu_boost_input_connect, + .disconnect = cpu_boost_input_disconnect, + .name = "cpu_input_boost", + .id_table = cpu_boost_ids, +}; + +static int __init cpu_boost_init(void) +{ + struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(0); + int maxfreq = cpufreq_quick_get_max(0); + int b_level = 0, req_freq[3]; + int curr, prev, i, ret = 1; + + if (!maxfreq) { + pr_err("Failed to get max freq, input boost disabled\n"); + goto err; + } + + for (i = 0; i < 3; i++) + req_freq[i] = maxfreq * boost_factor[i] / BOOST_FACTOR_DIVISOR; + + for (i = 0;; i++) { + curr = table[i].frequency - req_freq[b_level]; + prev = table[i ? i - 1 : 0].frequency - req_freq[b_level]; + + if (!curr || (curr > 0 && prev < 0)) { + boost_freq[b_level] = table[i].frequency; + b_level++; + } + + if (b_level == 3) + break; + } + + boost_wq = alloc_workqueue("cpu_input_boost_wq", WQ_HIGHPRI | WQ_NON_REENTRANT, 0); + if (!boost_wq) { + pr_err("Failed to allocate workqueue\n"); + ret = -EFAULT; + goto err; + } + + cpufreq_register_notifier(&cpu_do_boost_nb, CPUFREQ_POLICY_NOTIFIER); + + INIT_DELAYED_WORK(&restore_work, cpu_restore_main); + INIT_WORK(&boost_work, cpu_boost_main); + + ret = input_register_handler(&cpu_boost_input_handler); + if (ret) { + pr_err("Failed to register input handler, err: %d\n", ret); + goto err; + } + + register_early_suspend(&cpu_boost_early_suspend_handler); +err: + return ret; +} +late_initcall(cpu_boost_init); + +MODULE_AUTHOR("Sultanxda "); +MODULE_DESCRIPTION("CPU Input Boost"); +MODULE_LICENSE("GPLv2"); diff --git a/drivers/input/touchscreen/touchboost.c b/drivers/input/touchscreen/touchboost.c index 8cf93163682..b3f592bed93 100644 --- a/drivers/input/touchscreen/touchboost.c +++ b/drivers/input/touchscreen/touchboost.c @@ -14,53 +14,45 @@ #include #include #include -#include #include #include #include -#include -#include -#include #include #include +#include -#define MIM_TIME_INTERVAL_US (150 * USEC_PER_MSEC) +#define MIN_TIME_INTERVAL_US (50 * USEC_PER_MSEC) + +struct touchboost_inputopen { + struct input_handle *handle; + struct work_struct inputopen_work; +} touchboost_inputopen; /* * Use this variable in your governor of choice to calculate when the cpufreq * core is allowed to ramp the cpu down after an input event. That logic is done * by you, this var only outputs the last time in us an event was captured */ -u64 last_input_time; +static u64 last_input_time = 0; -struct touchboost_inputopen { - struct input_handle *handle; - struct work_struct inputopen_work; -} touchboost_inputopen; +inline u64 get_input_time(void) +{ + return last_input_time; +} static void boost_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { u64 now; - now = ktime_to_us(ktime_get()); + if ((type == EV_ABS)) { + now = ktime_to_us(ktime_get()); - if (now - last_input_time < MIM_TIME_INTERVAL_US) - return; + if (now - last_input_time < MIN_TIME_INTERVAL_US) + return; - last_input_time = ktime_to_us(ktime_get()); -} - -static void boost_input_open(struct work_struct *w) -{ - struct touchboost_inputopen *io = - container_of(w, struct touchboost_inputopen, inputopen_work); - - int error; - - error = input_open_device(io->handle); - if (error) - input_unregister_handle(io->handle); + last_input_time = ktime_to_us(ktime_get()); + } } static int boost_input_connect(struct input_handler *handler, @@ -69,20 +61,24 @@ static int boost_input_connect(struct input_handler *handler, struct input_handle *handle; int error; - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) return -ENOMEM; handle->dev = dev; handle->handler = handler; - handle->name = "touchboost"; + handle->name = handler->name; error = input_register_handle(handle); if (error) goto err; - touchboost_inputopen.handle = handle; - schedule_work(&touchboost_inputopen.inputopen_work); + error = input_open_device(handle); + if (error) { + input_unregister_handle(handle); + goto err; + } + return 0; err: @@ -98,27 +94,13 @@ static void boost_input_disconnect(struct input_handle *handle) } static const struct input_device_id boost_ids[] = { - /* multi-touch touchscreen */ { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | - INPUT_DEVICE_ID_MATCH_ABSBIT, + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, .evbit = { BIT_MASK(EV_ABS) }, + /* assumption: MT_.._X & MT_.._Y are in the same long */ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = - BIT_MASK(ABS_MT_POSITION_X) | - BIT_MASK(ABS_MT_POSITION_Y) }, - }, - /* touchpad */ - { - .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | - INPUT_DEVICE_ID_MATCH_ABSBIT, - .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, - .absbit = { [BIT_WORD(ABS_X)] = - BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, - }, - /* Keypad */ - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT, - .evbit = { BIT_MASK(EV_KEY) }, + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, }, { }, }; @@ -131,11 +113,10 @@ static struct input_handler boost_input_handler = { .id_table = boost_ids, }; -static int init(void) +static int __init init(void) { - INIT_WORK(&touchboost_inputopen.inputopen_work, boost_input_open); - - input_register_handler(&boost_input_handler); + if (input_register_handler(&boost_input_handler)) + pr_info("Unable to register the input handler\n"); return 0; } diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index e95c000bfd4..ffb18a5a7e5 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -50,7 +50,6 @@ #include "dc_reg.h" #include "dc_priv.h" #include "nvsd.h" -#include #define TEGRA_CRC_LATCHED_DELAY 34 @@ -66,7 +65,6 @@ static int no_vsync; static struct timeval t_suspend; -#define grouper_lvds_shutdown 110 static void _tegra_dc_controller_disable(struct tegra_dc *dc); @@ -2666,8 +2664,6 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc) } } - gpio_set_value(grouper_lvds_shutdown, 0); - if (dc->out_ops && dc->out_ops->disable) dc->out_ops->disable(dc); diff --git a/include/linux/touchboost.h b/include/linux/touchboost.h new file mode 100644 index 00000000000..1e7af26a21d --- /dev/null +++ b/include/linux/touchboost.h @@ -0,0 +1,15 @@ +/* + * linux/include/linux/touchboost.h + * + * franciscofranco.1990@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _LINUX_TOUCHBOOST_H +#define _LINUX_TOUCHBOOST_H + +u64 get_input_time(void); + +#endif diff --git a/kernel/sched_features.h b/kernel/sched_features.h index 48e69155111..0d6d7348ce0 100644 --- a/kernel/sched_features.h +++ b/kernel/sched_features.h @@ -47,7 +47,7 @@ SCHED_FEAT(CACHE_HOT_BUDDY, 1) /* * Use arch dependent cpu power functions */ -SCHED_FEAT(ARCH_POWER, 0) +SCHED_FEAT(ARCH_POWER, 1) SCHED_FEAT(HRTICK, 0) SCHED_FEAT(DOUBLE_TICK, 0)