From bb39f46cce4765d03ddabd65501fc31c6bbdcc93 Mon Sep 17 00:00:00 2001 From: Naitik2015 Date: Sat, 23 May 2015 17:28:03 +0530 Subject: [PATCH 001/118] Graphite , Ofast and Cortex A15 Optimization --- Makefile | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e97ef64938cf..3de37d3d039e 100644 --- a/Makefile +++ b/Makefile @@ -243,10 +243,14 @@ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ else echo sh; fi ; fi) -HOSTCC = gcc -HOSTCXX = g++ -HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -HOSTCXXFLAGS = -O2 +HOSTCC = gcc +HOSTCXX = g++ +HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -Ofast -fomit-frame-pointer -fgcse-las +HOSTCXXFLAGS = -Ofast -fgcse-las + +# Optimizations +HOSTCXXFLAGS += -fgraphite -floop-flatten -floop-parallelize-all -ftree-loop-linear -floop-interchange -floop-strip-mine -floop-block +HOSTCFLAGS += -fgraphite -floop-flatten -floop-parallelize-all -ftree-loop-linear -floop-interchange -floop-strip-mine -floop-block # Decide whether to build built-in, modular, or both. # Normally, just do built-in. @@ -347,10 +351,10 @@ CHECK = sparse CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ -Wbitwise -Wno-return-void $(CF) -CFLAGS_MODULE = +CFLAGS_MODULE = -mcpu=cortex-a15 -mtune=cortex-a15 -mfpu=neon-vfpv4 AFLAGS_MODULE = LDFLAGS_MODULE = -CFLAGS_KERNEL = +CFLAGS_KERNEL = -mcpu=cortex-a15 -mtune=cortex-a15 -mfpu=neon-vfpv4 AFLAGS_KERNEL = CFLAGS_GCOV = -fprofile-arcs -ftest-coverage From 9bcdb84e50bc3e64602225c609a9e4b65489f3bb Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Sat, 2 Nov 2013 19:04:08 -0500 Subject: [PATCH 002/118] Sound Control: WCD9320: expose additional controls Signed-off-by: Paul Reioux --- sound/soc/codecs/wcd9320.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c index b12481ef5f29..b7114d4ddf9a 100644 --- a/sound/soc/codecs/wcd9320.c +++ b/sound/soc/codecs/wcd9320.c @@ -4279,7 +4279,10 @@ static int taiko_volatile(struct snd_soc_codec *ssc, unsigned int reg) return 0; } -static int taiko_write(struct snd_soc_codec *codec, unsigned int reg, +#ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL +static +#endif +int taiko_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { int ret; @@ -4299,7 +4302,14 @@ static int taiko_write(struct snd_soc_codec *codec, unsigned int reg, return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value); } -static unsigned int taiko_read(struct snd_soc_codec *codec, +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +EXPORT_SYMBOL(taiko_write); +#endif + +#ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL +static +#endif +unsigned int taiko_read(struct snd_soc_codec *codec, unsigned int reg) { unsigned int val; @@ -4325,6 +4335,9 @@ static unsigned int taiko_read(struct snd_soc_codec *codec, val = wcd9xxx_reg_read(&wcd9xxx->core_res, reg); return val; } +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +EXPORT_SYMBOL(taiko_read); +#endif static int taiko_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -6996,6 +7009,11 @@ static struct regulator *taiko_codec_find_regulator(struct snd_soc_codec *codec, return NULL; } +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +struct snd_soc_codec *fauxsound_codec_ptr; +EXPORT_SYMBOL(fauxsound_codec_ptr); +#endif + static int taiko_codec_probe(struct snd_soc_codec *codec) { struct wcd9xxx *control; @@ -7009,6 +7027,11 @@ static int taiko_codec_probe(struct snd_soc_codec *codec) struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); struct wcd9xxx_core_resource *core_res; +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL + pr_info("taiko codec probe...\n"); + fauxsound_codec_ptr = codec; +#endif + codec->control_data = dev_get_drvdata(codec->dev->parent); control = codec->control_data; From 04ec64cccf010100fb56245dd6c0dbec42ad4022 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Mon, 23 Sep 2013 17:53:51 -0500 Subject: [PATCH 003/118] sound control 3.x: Initial GPL release for WCD9320 Audio Codec This is first official GPL release based on my private implementation. This release has been tested for xperia Z officially. It may work with other devices using the same WCD9320 Audio Codec as well, but not tested Signed-off-by: Paul Reioux --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 1 + sound/soc/codecs/sound_control_3_gpl.c | 276 +++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 sound/soc/codecs/sound_control_3_gpl.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ff72f77df66d..dd409a9e5fc6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -464,3 +464,9 @@ config SND_SOC_MSM_HDMI_CODEC_RX help HDMI audio drivers should be built only if the platform supports hdmi panel. + +config SOUND_CONTROL_HAX_3_GPL + tristate "new wcd93xx sound control hax" + default y + help + FauxSound WCD93xx chipset sound control hacks 3.0 for deeper hax diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4bf0a3c8486a..a3dd0a92db06 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -220,3 +220,4 @@ obj-$(CONFIG_SND_SOC_TFA98XX) += snd-soc-tfa98xx.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-$(CONFIG_SOUND_CONTROL_HAX_3_GPL) += sound_control_3_gpl.o diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c new file mode 100644 index 000000000000..08c54ebe9600 --- /dev/null +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -0,0 +1,276 @@ +/* + * Author: Paul Reioux aka Faux123 + * + * WCD93xx sound control module + * Copyright 2013 Paul Reioux + * + * 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 +#include +#include +#include +#include + +#define SOUND_CONTROL_MAJOR_VERSION 3 +#define SOUND_CONTROL_MINOR_VERSION 0 + +extern struct snd_soc_codec *fauxsound_codec_ptr; + +unsigned int taiko_read(struct snd_soc_codec *codec, unsigned int reg); +int taiko_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value); + +static bool calc_checksum(unsigned int a, unsigned int b, unsigned int c) +{ + unsigned char chksum = 0; + + chksum = ~((a & 0xff) + (b & 0xff)); + + if (chksum == (c & 0xff)) { + return true; + } else { + return false; + } +} + +static ssize_t cam_mic_gain_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u", + taiko_read(fauxsound_codec_ptr, + TAIKO_A_CDC_TX6_VOL_CTL_GAIN)); + +} + +static ssize_t cam_mic_gain_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int lval, chksum; + + sscanf(buf, "%u %u", &lval, &chksum); + + if (calc_checksum(lval, 0, chksum)) { + taiko_write(fauxsound_codec_ptr, + TAIKO_A_CDC_TX6_VOL_CTL_GAIN, lval); + } + return count; +} + +static ssize_t mic_gain_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u", + taiko_read(fauxsound_codec_ptr, + TAIKO_A_CDC_TX7_VOL_CTL_GAIN)); +} + +static ssize_t mic_gain_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int lval, chksum; + + sscanf(buf, "%u %u", &lval, &chksum); + + if (calc_checksum(lval, 0, chksum)) { + taiko_write(fauxsound_codec_ptr, + TAIKO_A_CDC_TX7_VOL_CTL_GAIN, lval); + } + return count; + +} + +static ssize_t speaker_gain_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u %u", + taiko_read(fauxsound_codec_ptr, + TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL), + taiko_read(fauxsound_codec_ptr, + TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL)); + +} + +static ssize_t speaker_gain_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int lval, rval, chksum; + + sscanf(buf, "%u %u %u", &lval, &rval, &chksum); + + if (calc_checksum(lval, rval, chksum)) { + taiko_write(fauxsound_codec_ptr, + TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL, lval); + taiko_write(fauxsound_codec_ptr, + TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL, rval); + } + return count; +} + +static ssize_t headphone_gain_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u %u", + taiko_read(fauxsound_codec_ptr, + TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL), + taiko_read(fauxsound_codec_ptr, + TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL)); +} + +static ssize_t headphone_gain_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int lval, rval, chksum; + + sscanf(buf, "%u %u %u", &lval, &rval, &chksum); + + if (calc_checksum(lval, rval, chksum)) { + taiko_write(fauxsound_codec_ptr, + TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL, lval); + taiko_write(fauxsound_codec_ptr, + TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL, rval); + } + return count; +} + +static ssize_t headphone_pa_gain_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u %u", + taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN), + taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN)); +} + +static ssize_t headphone_pa_gain_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int lval, rval, chksum; + unsigned int gain, status; + unsigned int out; + + sscanf(buf, "%u %u %u", &lval, &rval, &chksum); + + if (calc_checksum(lval, rval, chksum)) { + gain = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN); + out = (gain & 0xf0) | lval; + taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN, out); + + status = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_STATUS); + out = (status & 0x0f) | (lval << 4); + taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_STATUS, out); + + gain = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN); + out = (gain & 0xf0) | rval; + taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN, out); + + status = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_STATUS); + out = (status & 0x0f) | (rval << 4); + taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_STATUS, out); + } + return count; +} + +static ssize_t sound_control_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "version: %u.%u\n", + SOUND_CONTROL_MAJOR_VERSION, + SOUND_CONTROL_MINOR_VERSION); +} + +static struct kobj_attribute cam_mic_gain_attribute = + __ATTR(gpl_cam_mic_gain, + 0666, + cam_mic_gain_show, + cam_mic_gain_store); + +static struct kobj_attribute mic_gain_attribute = + __ATTR(gpl_mic_gain, + 0666, + mic_gain_show, + mic_gain_store); + +static struct kobj_attribute speaker_gain_attribute = + __ATTR(gpl_speaker_gain, + 0666, + speaker_gain_show, + speaker_gain_store); + +static struct kobj_attribute headphone_gain_attribute = + __ATTR(gpl_headphone_gain, + 0666, + headphone_gain_show, + headphone_gain_store); + +static struct kobj_attribute headphone_pa_gain_attribute = + __ATTR(gpl_headphone_pa_gain, + 0666, + headphone_pa_gain_show, + headphone_pa_gain_store); + +static struct kobj_attribute sound_control_version_attribute = + __ATTR(gpl_sound_control_version, + 0444, + sound_control_version_show, NULL); + +static struct attribute *sound_control_attrs[] = + { + &cam_mic_gain_attribute.attr, + &mic_gain_attribute.attr, + &speaker_gain_attribute.attr, + &headphone_gain_attribute.attr, + &headphone_pa_gain_attribute.attr, + &sound_control_version_attribute.attr, + NULL, + }; + +static struct attribute_group sound_control_attr_group = + { + .attrs = sound_control_attrs, + }; + +static struct kobject *sound_control_kobj; + +static int sound_control_init(void) +{ + int sysfs_result; + + sound_control_kobj = + kobject_create_and_add("sound_control_3", kernel_kobj); + + if (!sound_control_kobj) { + pr_err("%s sound_control_kobj create failed!\n", + __FUNCTION__); + return -ENOMEM; + } + + sysfs_result = sysfs_create_group(sound_control_kobj, + &sound_control_attr_group); + + if (sysfs_result) { + pr_info("%s sysfs create failed!\n", __FUNCTION__); + kobject_put(sound_control_kobj); + } + return sysfs_result; +} + +static void sound_control_exit(void) +{ + if (sound_control_kobj != NULL) + kobject_put(sound_control_kobj); +} + +module_init(sound_control_init); +module_exit(sound_control_exit); +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Paul Reioux "); +MODULE_DESCRIPTION("Sound Control Module 3.x"); + From e05ed54ab07cd210a99c4bb15b9216abc8a4780d Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Mon, 23 Sep 2013 19:28:53 -0500 Subject: [PATCH 004/118] sound control: WCD9320: update speaker gain control for Z1 Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index 08c54ebe9600..8e497e0aa731 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -94,9 +94,9 @@ static ssize_t speaker_gain_show(struct kobject *kobj, { return sprintf(buf, "%u %u", taiko_read(fauxsound_codec_ptr, - TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL), + TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL), taiko_read(fauxsound_codec_ptr, - TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL)); + TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL)); } @@ -109,9 +109,9 @@ static ssize_t speaker_gain_store(struct kobject *kobj, if (calc_checksum(lval, rval, chksum)) { taiko_write(fauxsound_codec_ptr, - TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL, lval); + TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL, lval); taiko_write(fauxsound_codec_ptr, - TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL, rval); + TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL, rval); } return count; } From 95eae9928a1c2b52b4741944530c1af8d7ffd28a Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Sun, 17 Nov 2013 16:47:38 -0600 Subject: [PATCH 005/118] Sound Control: (Optional) work around for Nexus 4/5 audio issues (Use this only for devices with audio reset issues) Also bump version to 3.1 Signed-off-by: Paul Reioux wcd9xxx-core: add register write without mutex protection This is assuming the calling function will take care of the mutex. Signed-off-by: Paul Reioux --- drivers/mfd/wcd9xxx-core.c | 16 ++++++ include/linux/mfd/wcd9xxx/core.h | 3 ++ sound/soc/codecs/sound_control_3_gpl.c | 73 +++++++++++++++++++++++--- sound/soc/codecs/wcd9320.c | 61 +++++++++++++-------- 4 files changed, 124 insertions(+), 29 deletions(-) diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c index 156ce24e0920..e2d573a06fff 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/drivers/mfd/wcd9xxx-core.c @@ -119,6 +119,22 @@ int wcd9xxx_reg_read( } EXPORT_SYMBOL(wcd9xxx_reg_read); +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +int wcd9xxx_reg_read_safe(struct wcd9xxx *wcd9xxx, unsigned short reg) +{ + u8 val; + int ret; + + ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, false); + + if (ret < 0) + return ret; + else + return val; +} +EXPORT_SYMBOL_GPL(wcd9xxx_reg_read_safe); +#endif + static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, unsigned short reg, int bytes, void *src, bool interface_reg) { diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h index a8e792b093a4..c2e050c4c0cd 100644 --- a/include/linux/mfd/wcd9xxx/core.h +++ b/include/linux/mfd/wcd9xxx/core.h @@ -196,6 +196,9 @@ struct wcd9xxx { }; int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg); +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +int wcd9xxx_reg_read_safe(struct wcd9xxx *wcd9xxx, unsigned short reg); +#endif int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, u8 val); int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la); diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index 8e497e0aa731..ed851f2d40da 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -22,14 +22,48 @@ #include #define SOUND_CONTROL_MAJOR_VERSION 3 -#define SOUND_CONTROL_MINOR_VERSION 0 +#define SOUND_CONTROL_MINOR_VERSION 1 extern struct snd_soc_codec *fauxsound_codec_ptr; +static int snd_ctrl_locked = 0; + unsigned int taiko_read(struct snd_soc_codec *codec, unsigned int reg); int taiko_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); +int reg_access(unsigned int reg) +{ + int ret = 1; + + switch (reg) { + case TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL: + case TAIKO_A_CDC_TX1_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX2_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX3_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX4_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX5_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX6_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX7_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX8_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX9_VOL_CTL_GAIN: + case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: + if (snd_ctrl_locked) + ret = 0; + break; + default: + break; + } + return ret; +} +EXPORT_SYMBOL(reg_access); + static bool calc_checksum(unsigned int a, unsigned int b, unsigned int c) { unsigned char chksum = 0; @@ -46,7 +80,7 @@ static bool calc_checksum(unsigned int a, unsigned int b, unsigned int c) static ssize_t cam_mic_gain_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%u", + return sprintf(buf, "%u\n", taiko_read(fauxsound_codec_ptr, TAIKO_A_CDC_TX6_VOL_CTL_GAIN)); @@ -69,7 +103,7 @@ static ssize_t cam_mic_gain_store(struct kobject *kobj, static ssize_t mic_gain_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%u", + return sprintf(buf, "%u\n", taiko_read(fauxsound_codec_ptr, TAIKO_A_CDC_TX7_VOL_CTL_GAIN)); } @@ -92,7 +126,7 @@ static ssize_t mic_gain_store(struct kobject *kobj, static ssize_t speaker_gain_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%u %u", + return sprintf(buf, "%u %u\n", taiko_read(fauxsound_codec_ptr, TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL), taiko_read(fauxsound_codec_ptr, @@ -119,7 +153,7 @@ static ssize_t speaker_gain_store(struct kobject *kobj, static ssize_t headphone_gain_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%u %u", + return sprintf(buf, "%u %u\n", taiko_read(fauxsound_codec_ptr, TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL), taiko_read(fauxsound_codec_ptr, @@ -145,7 +179,7 @@ static ssize_t headphone_gain_store(struct kobject *kobj, static ssize_t headphone_pa_gain_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%u %u", + return sprintf(buf, "%u %u\n", taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN), taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN)); } @@ -186,6 +220,26 @@ static ssize_t sound_control_version_show(struct kobject *kobj, struct kobj_attr SOUND_CONTROL_MINOR_VERSION); } +static ssize_t sound_control_locked_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int inp; + + sscanf(buf, "%d", &inp); + + if (inp == 0) + snd_ctrl_locked = 0; + else + snd_ctrl_locked = 1; + + return count; +} + +static ssize_t sound_control_locked_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", snd_ctrl_locked); +} + static struct kobj_attribute cam_mic_gain_attribute = __ATTR(gpl_cam_mic_gain, 0666, @@ -216,6 +270,12 @@ static struct kobj_attribute headphone_pa_gain_attribute = headphone_pa_gain_show, headphone_pa_gain_store); +static struct kobj_attribute sound_control_locked_attribute = + __ATTR(gpl_sound_control_locked, + 0666, + sound_control_locked_show, + sound_control_locked_store); + static struct kobj_attribute sound_control_version_attribute = __ATTR(gpl_sound_control_version, 0444, @@ -228,6 +288,7 @@ static struct attribute *sound_control_attrs[] = &speaker_gain_attribute.attr, &headphone_gain_attribute.attr, &headphone_pa_gain_attribute.attr, + &sound_control_locked_attribute.attr, &sound_control_version_attribute.attr, NULL, }; diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c index b7114d4ddf9a..20102a24e960 100644 --- a/sound/soc/codecs/wcd9320.c +++ b/sound/soc/codecs/wcd9320.c @@ -4279,12 +4279,13 @@ static int taiko_volatile(struct snd_soc_codec *ssc, unsigned int reg) return 0; } -#ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL +#ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL static #endif -int taiko_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +unsigned int taiko_read(struct snd_soc_codec *codec, + unsigned int reg) { + unsigned int val; int ret; struct wcd9xxx *wcd9xxx = codec->control_data; @@ -4293,27 +4294,37 @@ int taiko_write(struct snd_soc_codec *codec, unsigned int reg, BUG_ON(reg > TAIKO_MAX_REGISTER); - if (!taiko_volatile(codec, reg)) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret != 0) - dev_err(codec->dev, "Cache write to %x failed: %d\n", + if (!taiko_volatile(codec, reg) && taiko_readable(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_read(codec, reg, &val); + if (ret >= 0) { + return val; + } else + dev_err(codec->dev, "Cache read from %x failed: %d\n", reg, ret); } - return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value); + val = wcd9xxx_reg_read(&wcd9xxx->core_res, reg); + return val; } #ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL -EXPORT_SYMBOL(taiko_write); +EXPORT_SYMBOL(taiko_read); #endif -#ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +extern int reg_access(unsigned int); +#endif + +#ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL static #endif -unsigned int taiko_read(struct snd_soc_codec *codec, - unsigned int reg) +int taiko_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) { - unsigned int val; int ret; +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL + int val; +#endif struct wcd9xxx *wcd9xxx = codec->control_data; @@ -4322,21 +4333,25 @@ unsigned int taiko_read(struct snd_soc_codec *codec, BUG_ON(reg > TAIKO_MAX_REGISTER); - if (!taiko_volatile(codec, reg) && taiko_readable(codec, reg) && - reg < codec->driver->reg_cache_size) { - ret = snd_soc_cache_read(codec, reg, &val); - if (ret >= 0) { - return val; - } else - dev_err(codec->dev, "Cache read from %x failed: %d\n", + if (!taiko_volatile(codec, reg)) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret != 0) + dev_err(codec->dev, "Cache write to %x failed: %d\n", reg, ret); } - val = wcd9xxx_reg_read(&wcd9xxx->core_res, reg); - return val; +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL + if (!reg_access(reg)) + val = wcd9xxx_reg_read_safe(codec->control_data, reg); + else + val = value; + return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, val); +#else + return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value); +#endif } #ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL -EXPORT_SYMBOL(taiko_read); +EXPORT_SYMBOL(taiko_write); #endif static int taiko_startup(struct snd_pcm_substream *substream, From 80a94a73853bd7509aee72d2c0c0d45a25d95925 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Wed, 20 Nov 2013 01:07:50 -0600 Subject: [PATCH 006/118] Sound Control: (OPTIONAL) add power amp registers access control as well Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index ed851f2d40da..e6707f0a233f 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -37,6 +37,10 @@ int reg_access(unsigned int reg) int ret = 1; switch (reg) { + case TAIKO_A_RX_HPH_L_GAIN: + case TAIKO_A_RX_HPH_R_GAIN: + case TAIKO_A_RX_HPH_L_STATUS: + case TAIKO_A_RX_HPH_R_STATUS: case TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL: From a39f39eb29c2cb4a19ba541ecf1ac6d58c443608 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Fri, 29 Nov 2013 16:32:27 -0600 Subject: [PATCH 007/118] sound control: add register cache bump to version 3.2 Signed-off-by: Paul Reioux Conflicts: sound/soc/codecs/wcd9320.c --- sound/soc/codecs/sound_control_3_gpl.c | 100 ++++++++++++++++++++++++- sound/soc/codecs/wcd9320.c | 20 +++-- 2 files changed, 109 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index e6707f0a233f..4628e1a76a94 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -22,7 +22,9 @@ #include #define SOUND_CONTROL_MAJOR_VERSION 3 -#define SOUND_CONTROL_MINOR_VERSION 1 +#define SOUND_CONTROL_MINOR_VERSION 2 + +#define REG_SZ 21 extern struct snd_soc_codec *fauxsound_codec_ptr; @@ -32,7 +34,96 @@ unsigned int taiko_read(struct snd_soc_codec *codec, unsigned int reg); int taiko_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); -int reg_access(unsigned int reg) + +static unsigned int cached_regs[] = {6, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned int *cache_select(unsigned int reg) +{ + unsigned int *out = NULL; + + switch (reg) { + case TAIKO_A_RX_HPH_L_GAIN: + out = &cached_regs[0]; + break; + case TAIKO_A_RX_HPH_R_GAIN: + out = &cached_regs[1]; + break; + case TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL: + out = &cached_regs[4]; + break; + case TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL: + out = &cached_regs[5]; + break; + case TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL: + out = &cached_regs[6]; + break; + case TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL: + out = &cached_regs[7]; + break; + case TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL: + out = &cached_regs[8]; + break; + case TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL: + out = &cached_regs[9]; + break; + case TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL: + out = &cached_regs[10]; + break; + case TAIKO_A_CDC_TX1_VOL_CTL_GAIN: + out = &cached_regs[11]; + break; + case TAIKO_A_CDC_TX2_VOL_CTL_GAIN: + out = &cached_regs[12]; + break; + case TAIKO_A_CDC_TX3_VOL_CTL_GAIN: + out = &cached_regs[13]; + break; + case TAIKO_A_CDC_TX4_VOL_CTL_GAIN: + out = &cached_regs[14]; + break; + case TAIKO_A_CDC_TX5_VOL_CTL_GAIN: + out = &cached_regs[15]; + break; + case TAIKO_A_CDC_TX6_VOL_CTL_GAIN: + out = &cached_regs[16]; + break; + case TAIKO_A_CDC_TX7_VOL_CTL_GAIN: + out = &cached_regs[17]; + break; + case TAIKO_A_CDC_TX8_VOL_CTL_GAIN: + out = &cached_regs[18]; + break; + case TAIKO_A_CDC_TX9_VOL_CTL_GAIN: + out = &cached_regs[19]; + break; + case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: + out = &cached_regs[20]; + break; + } + return out; +} + +void snd_hax_cache_write(unsigned int reg, unsigned int value) +{ + unsigned int *tmp = cache_select(reg); + + if (tmp != NULL) + *tmp = value; +} +EXPORT_SYMBOL(snd_hax_cache_write); + +unsigned int snd_hax_cache_read(unsigned int reg) +{ + if (cache_select(reg) != NULL) + return *cache_select(reg); + else + return -1; +} +EXPORT_SYMBOL(snd_hax_cache_read); + +int snd_hax_reg_access(unsigned int reg) { int ret = 1; @@ -66,7 +157,7 @@ int reg_access(unsigned int reg) } return ret; } -EXPORT_SYMBOL(reg_access); +EXPORT_SYMBOL(snd_hax_reg_access); static bool calc_checksum(unsigned int a, unsigned int b, unsigned int c) { @@ -217,7 +308,8 @@ static ssize_t headphone_pa_gain_store(struct kobject *kobj, return count; } -static ssize_t sound_control_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +static ssize_t sound_control_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { return sprintf(buf, "version: %u.%u\n", SOUND_CONTROL_MAJOR_VERSION, diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c index 20102a24e960..fb86eaf30796 100644 --- a/sound/soc/codecs/wcd9320.c +++ b/sound/soc/codecs/wcd9320.c @@ -4279,6 +4279,12 @@ static int taiko_volatile(struct snd_soc_codec *ssc, unsigned int reg) return 0; } +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL +extern int snd_hax_reg_access(unsigned int); +extern unsigned int snd_hax_cache_read(unsigned int); +extern void snd_hax_cache_write(unsigned int, unsigned int); +#endif + #ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL static #endif @@ -4311,10 +4317,6 @@ unsigned int taiko_read(struct snd_soc_codec *codec, EXPORT_SYMBOL(taiko_read); #endif -#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL -extern int reg_access(unsigned int); -#endif - #ifndef CONFIG_SOUND_CONTROL_HAX_3_GPL static #endif @@ -4341,10 +4343,14 @@ int taiko_write(struct snd_soc_codec *codec, unsigned int reg, } #ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL - if (!reg_access(reg)) - val = wcd9xxx_reg_read_safe(codec->control_data, reg); - else + if (!snd_hax_reg_access(reg)) { + if (!((val = snd_hax_cache_read(reg)) != -1)) { + val = wcd9xxx_reg_read_safe(codec->control_data, reg); + } + } else { + snd_hax_cache_write(reg, value); val = value; + } return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, val); #else return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value); From 67d38fb34711432afac136bddac459fd1f78d78a Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Wed, 30 Apr 2014 11:05:36 -0500 Subject: [PATCH 008/118] sound control: Samsung Microphone compatibility fixup Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index 4628e1a76a94..f7088648fdcf 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -139,6 +139,7 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL: +/* case TAIKO_A_CDC_TX1_VOL_CTL_GAIN: case TAIKO_A_CDC_TX2_VOL_CTL_GAIN: case TAIKO_A_CDC_TX3_VOL_CTL_GAIN: @@ -149,6 +150,7 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_TX8_VOL_CTL_GAIN: case TAIKO_A_CDC_TX9_VOL_CTL_GAIN: case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: +*/ if (snd_ctrl_locked) ret = 0; break; From 9823ba87322029ce4e2e894fac9075bb67a75cd6 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Wed, 9 Jul 2014 18:43:43 -0500 Subject: [PATCH 009/118] Sound Control: expose direct register manipulations to userspace bump driver version to 3.3 Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 56 +++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index f7088648fdcf..dd424ddd0ae6 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -22,7 +22,7 @@ #include #define SOUND_CONTROL_MAJOR_VERSION 3 -#define SOUND_CONTROL_MINOR_VERSION 2 +#define SOUND_CONTROL_MINOR_VERSION 3 #define REG_SZ 21 @@ -310,6 +310,39 @@ static ssize_t headphone_pa_gain_store(struct kobject *kobj, return count; } +static unsigned int selected_reg = 0xdeadbeef; + +static ssize_t sound_reg_select_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + sscanf(buf, "%u", &selected_reg); + + return count; +} + +static ssize_t sound_reg_read_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + if (selected_reg == 0xdeadbeef) + return -1; + else + return sprintf(buf, "%u\n", + taiko_read(fauxsound_codec_ptr, selected_reg)); +} + +static ssize_t sound_reg_write_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int out, chksum; + + sscanf(buf, "%u %u", &out, &chksum); + if (calc_checksum(out, 0, chksum)) { + if (selected_reg != 0xdeadbeef) + taiko_write(fauxsound_codec_ptr, selected_reg, out); + } + return count; +} + static ssize_t sound_control_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -338,6 +371,24 @@ static ssize_t sound_control_locked_show(struct kobject *kobj, struct kobj_attri return sprintf(buf, "%d\n", snd_ctrl_locked); } +static struct kobj_attribute sound_reg_sel_attribute = + __ATTR(sound_reg_sel, + 0222, + NULL, + sound_reg_select_store); + +static struct kobj_attribute sound_reg_read_attribute = + __ATTR(sound_reg_read, + 0444, + sound_reg_read_show, + NULL); + +static struct kobj_attribute sound_reg_write_attribute = + __ATTR(sound_reg_write, + 0222, + NULL, + sound_reg_write_store); + static struct kobj_attribute cam_mic_gain_attribute = __ATTR(gpl_cam_mic_gain, 0666, @@ -387,6 +438,9 @@ static struct attribute *sound_control_attrs[] = &headphone_gain_attribute.attr, &headphone_pa_gain_attribute.attr, &sound_control_locked_attribute.attr, + &sound_reg_sel_attribute.attr, + &sound_reg_read_attribute.attr, + &sound_reg_write_attribute.attr, &sound_control_version_attribute.attr, NULL, }; From 4a9523f634d0ed5ab636b813cc2a41478fa157ab Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Sat, 26 Jul 2014 10:13:27 -0500 Subject: [PATCH 010/118] Sound Control: Misc clean up for newer WCD9xxx SOCs Bump driver version to 3.4 Change-Id: Idaf374634368e61ba1fc9e24d0c5e1a994271400 Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 31 ++++++++++++++++++++++---- sound/soc/codecs/wcd9320.c | 8 +++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index dd424ddd0ae6..6380216369fd 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -19,14 +19,16 @@ #include #include #include +#include #include #define SOUND_CONTROL_MAJOR_VERSION 3 -#define SOUND_CONTROL_MINOR_VERSION 3 +#define SOUND_CONTROL_MINOR_VERSION 4 #define REG_SZ 21 extern struct snd_soc_codec *fauxsound_codec_ptr; +extern int wcd9xxx_hw_revision; static int snd_ctrl_locked = 0; @@ -35,7 +37,7 @@ int taiko_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); -static unsigned int cached_regs[] = {6, 6, 0, 0, 0, 0, 0, 0, 0, 0, +static unsigned int cached_regs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -132,6 +134,10 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_RX_HPH_R_GAIN: case TAIKO_A_RX_HPH_L_STATUS: case TAIKO_A_RX_HPH_R_STATUS: + if (wcd9xxx_hw_revision == 1) + if (snd_ctrl_locked) + ret = 0; + break; case TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL: @@ -139,7 +145,9 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL: -/* + if (snd_ctrl_locked) + ret = 0; + break; case TAIKO_A_CDC_TX1_VOL_CTL_GAIN: case TAIKO_A_CDC_TX2_VOL_CTL_GAIN: case TAIKO_A_CDC_TX3_VOL_CTL_GAIN: @@ -150,7 +158,6 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_TX8_VOL_CTL_GAIN: case TAIKO_A_CDC_TX9_VOL_CTL_GAIN: case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: -*/ if (snd_ctrl_locked) ret = 0; break; @@ -293,18 +300,22 @@ static ssize_t headphone_pa_gain_store(struct kobject *kobj, if (calc_checksum(lval, rval, chksum)) { gain = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN); out = (gain & 0xf0) | lval; + if (wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN, out); status = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_STATUS); out = (status & 0x0f) | (lval << 4); + if (wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_STATUS, out); gain = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN); out = (gain & 0xf0) | rval; + if (wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN, out); status = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_STATUS); out = (status & 0x0f) | (rval << 4); + if(wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_STATUS, out); } return count; @@ -343,6 +354,12 @@ static ssize_t sound_reg_write_store(struct kobject *kobj, return count; } +static ssize_t sound_control_hw_revision_show (struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "hw_revision: %i\n", wcd9xxx_hw_revision); +} + static ssize_t sound_control_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -430,6 +447,11 @@ static struct kobj_attribute sound_control_version_attribute = 0444, sound_control_version_show, NULL); +static struct kobj_attribute sound_hw_revision_attribute = + __ATTR(gpl_sound_control_hw_revision, + 0444, + sound_control_hw_revision_show, NULL); + static struct attribute *sound_control_attrs[] = { &cam_mic_gain_attribute.attr, @@ -441,6 +463,7 @@ static struct attribute *sound_control_attrs[] = &sound_reg_sel_attribute.attr, &sound_reg_read_attribute.attr, &sound_reg_write_attribute.attr, + &sound_hw_revision_attribute.attr, &sound_control_version_attribute.attr, NULL, }; diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c index fb86eaf30796..032fcbce373a 100644 --- a/sound/soc/codecs/wcd9320.c +++ b/sound/soc/codecs/wcd9320.c @@ -7033,6 +7033,8 @@ static struct regulator *taiko_codec_find_regulator(struct snd_soc_codec *codec, #ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL struct snd_soc_codec *fauxsound_codec_ptr; EXPORT_SYMBOL(fauxsound_codec_ptr); +int wcd9xxx_hw_revision; +EXPORT_SYMBOL(wcd9xxx_hw_revision); #endif static int taiko_codec_probe(struct snd_soc_codec *codec) @@ -7056,6 +7058,12 @@ static int taiko_codec_probe(struct snd_soc_codec *codec) codec->control_data = dev_get_drvdata(codec->dev->parent); control = codec->control_data; +#ifdef CONFIG_SOUND_CONTROL_HAX_3_GPL + if (TAIKO_IS_1_0(control->version)) + wcd9xxx_hw_revision = 1; + else + wcd9xxx_hw_revision = 2; +#endif wcd9xxx_ssr_register(control, taiko_device_down, taiko_post_reset_cb, (void *)codec); From 319e3c541f16f09474e31e48fc703c6e2f602701 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Thu, 31 Jul 2014 08:17:42 -0500 Subject: [PATCH 011/118] Sound Control: let register lock be dependent on different HW revisions Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index 6380216369fd..a1eb26478a46 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -134,9 +134,8 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_RX_HPH_R_GAIN: case TAIKO_A_RX_HPH_L_STATUS: case TAIKO_A_RX_HPH_R_STATUS: - if (wcd9xxx_hw_revision == 1) - if (snd_ctrl_locked) - ret = 0; + if (snd_ctrl_locked > 1) + ret = 0; break; case TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL: @@ -145,7 +144,7 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL: - if (snd_ctrl_locked) + if (snd_ctrl_locked > 0) ret = 0; break; case TAIKO_A_CDC_TX1_VOL_CTL_GAIN: @@ -158,7 +157,7 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_TX8_VOL_CTL_GAIN: case TAIKO_A_CDC_TX9_VOL_CTL_GAIN: case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: - if (snd_ctrl_locked) + if (snd_ctrl_locked > 0) ret = 0; break; default: @@ -300,22 +299,18 @@ static ssize_t headphone_pa_gain_store(struct kobject *kobj, if (calc_checksum(lval, rval, chksum)) { gain = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN); out = (gain & 0xf0) | lval; - if (wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_GAIN, out); status = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_STATUS); out = (status & 0x0f) | (lval << 4); - if (wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_L_STATUS, out); gain = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN); out = (gain & 0xf0) | rval; - if (wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_GAIN, out); status = taiko_read(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_STATUS); out = (status & 0x0f) | (rval << 4); - if(wcd9xxx_hw_revision == 1) taiko_write(fauxsound_codec_ptr, TAIKO_A_RX_HPH_R_STATUS, out); } return count; @@ -375,10 +370,7 @@ static ssize_t sound_control_locked_store(struct kobject *kobj, sscanf(buf, "%d", &inp); - if (inp == 0) - snd_ctrl_locked = 0; - else - snd_ctrl_locked = 1; + snd_ctrl_locked = inp; return count; } From a11fb88a70810a3529d752ac1b1be4ae934171f6 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Thu, 31 Jul 2014 12:13:23 -0500 Subject: [PATCH 012/118] Sound Control: separate locking mechanism for recording and playback with newer hardware revisions coming from Qualcomm, single register lock control isn't sufficient to cover both playback and recording usage scenarios bump to version 3.5 Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 33 +++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index a1eb26478a46..9bff1c3f2772 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -23,7 +23,7 @@ #include #define SOUND_CONTROL_MAJOR_VERSION 3 -#define SOUND_CONTROL_MINOR_VERSION 4 +#define SOUND_CONTROL_MINOR_VERSION 5 #define REG_SZ 21 @@ -31,6 +31,7 @@ extern struct snd_soc_codec *fauxsound_codec_ptr; extern int wcd9xxx_hw_revision; static int snd_ctrl_locked = 0; +static int snd_rec_ctrl_locked = 0; unsigned int taiko_read(struct snd_soc_codec *codec, unsigned int reg); int taiko_write(struct snd_soc_codec *codec, unsigned int reg, @@ -157,7 +158,7 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_TX8_VOL_CTL_GAIN: case TAIKO_A_CDC_TX9_VOL_CTL_GAIN: case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: - if (snd_ctrl_locked > 0) + if (snd_rec_ctrl_locked > 0) ret = 0; break; default: @@ -375,11 +376,30 @@ static ssize_t sound_control_locked_store(struct kobject *kobj, return count; } -static ssize_t sound_control_locked_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +static ssize_t sound_control_locked_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", snd_ctrl_locked); } +static ssize_t sound_control_rec_locked_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int inp; + + sscanf(buf, "%d", &inp); + + snd_rec_ctrl_locked = inp; + + return count; +} + +static ssize_t sound_control_rec_locked_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", snd_rec_ctrl_locked); +} + static struct kobj_attribute sound_reg_sel_attribute = __ATTR(sound_reg_sel, 0222, @@ -434,6 +454,12 @@ static struct kobj_attribute sound_control_locked_attribute = sound_control_locked_show, sound_control_locked_store); +static struct kobj_attribute sound_control_rec_locked_attribute = + __ATTR(gpl_sound_control_rec_locked, + 0666, + sound_control_rec_locked_show, + sound_control_rec_locked_store); + static struct kobj_attribute sound_control_version_attribute = __ATTR(gpl_sound_control_version, 0444, @@ -452,6 +478,7 @@ static struct attribute *sound_control_attrs[] = &headphone_gain_attribute.attr, &headphone_pa_gain_attribute.attr, &sound_control_locked_attribute.attr, + &sound_control_rec_locked_attribute.attr, &sound_reg_sel_attribute.attr, &sound_reg_read_attribute.attr, &sound_reg_write_attribute.attr, From 319bf7d8af0f01e908cb11ef08f71323315a6d79 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Tue, 9 Sep 2014 00:01:43 -0500 Subject: [PATCH 013/118] Sound Control: add line gain to cached and register lock bump version to 3.6 Signed-off-by: Paul Reioux --- sound/soc/codecs/sound_control_3_gpl.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/sound_control_3_gpl.c b/sound/soc/codecs/sound_control_3_gpl.c index 9bff1c3f2772..f09be56cd88e 100644 --- a/sound/soc/codecs/sound_control_3_gpl.c +++ b/sound/soc/codecs/sound_control_3_gpl.c @@ -23,9 +23,7 @@ #include #define SOUND_CONTROL_MAJOR_VERSION 3 -#define SOUND_CONTROL_MINOR_VERSION 5 - -#define REG_SZ 21 +#define SOUND_CONTROL_MINOR_VERSION 6 extern struct snd_soc_codec *fauxsound_codec_ptr; extern int wcd9xxx_hw_revision; @@ -37,10 +35,10 @@ unsigned int taiko_read(struct snd_soc_codec *codec, unsigned int reg); int taiko_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); - +#define REG_SZ 25 static unsigned int cached_regs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0 }; + 0, 0, 0, 0, 0 }; static unsigned int *cache_select(unsigned int reg) { @@ -104,6 +102,18 @@ static unsigned int *cache_select(unsigned int reg) case TAIKO_A_CDC_TX10_VOL_CTL_GAIN: out = &cached_regs[20]; break; + case TAIKO_A_RX_LINE_1_GAIN: + out = &cached_regs[21]; + break; + case TAIKO_A_RX_LINE_2_GAIN: + out = &cached_regs[22]; + break; + case TAIKO_A_RX_LINE_3_GAIN: + out = &cached_regs[23]; + break; + case TAIKO_A_RX_LINE_4_GAIN: + out = &cached_regs[24]; + break; } return out; } @@ -145,6 +155,10 @@ int snd_hax_reg_access(unsigned int reg) case TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL: case TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL: + case TAIKO_A_RX_LINE_1_GAIN: + case TAIKO_A_RX_LINE_2_GAIN: + case TAIKO_A_RX_LINE_3_GAIN: + case TAIKO_A_RX_LINE_4_GAIN: if (snd_ctrl_locked > 0) ret = 0; break; From e642ebb946a8e9a48ea6607b38ff7ba1d83a912a Mon Sep 17 00:00:00 2001 From: savoca Date: Mon, 16 Feb 2015 01:13:19 +0000 Subject: [PATCH 014/118] msm: mdss: Add KCAL support for post processing control Conflicts: drivers/video/msm/mdss/Kconfig drivers/video/msm/mdss/Makefile drivers/video/msm/mdss/mdss_mdp_pp.c Conflicts: drivers/video/msm/mdss/Makefile drivers/video/msm/mdss/mdss_mdp_pp.c Conflicts: drivers/video/msm/mdss/mdss_mdp_pp.c Change-Id: I26a50aa6bfac69a6f5c8556f38ad9b6ba001374f --- drivers/video/msm/mdss/Kconfig | 7 + drivers/video/msm/mdss/Makefile | 2 + drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 391 ++++++++++++++++++++ drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h | 45 +++ drivers/video/msm/mdss/mdss_mdp_pp.c | 120 ++++++ 5 files changed, 565 insertions(+) create mode 100644 drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c create mode 100644 drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig index 4a73d8e990c9..9a94946d297f 100644 --- a/drivers/video/msm/mdss/Kconfig +++ b/drivers/video/msm/mdss/Kconfig @@ -53,3 +53,10 @@ config FB_MSM_MDSS_HDMI_MHL_SII8620_8061 Support the HDMI to MHL1/2/3 conversion. MHL (Mobile High-Definition Link) technology uses USB connector to output HDMI content + +config FB_MSM_MDSS_KCAL_CTRL + depends on FB_MSM_MDSS + bool "MDSS color control" + ---help--- + Enable sysfs for rgb/lut control for mdss-mdp display controllers + in the MDSS sub-system. diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile index 6b8ff1eb6137..08177368b01f 100644 --- a/drivers/video/msm/mdss/Makefile +++ b/drivers/video/msm/mdss/Makefile @@ -58,3 +58,5 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += mdss_dsi_status.o + +obj-$(CONFIG_FB_MSM_MDSS_KCAL_CTRL) += mdss_mdp_kcal_ctrl.o diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c new file mode 100644 index 000000000000..e9da0516b3ad --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, LGE Inc. All rights reserved + * Copyright (c) 2014 savoca + * Copyright (c) 2014 Paul Reioux + * + * 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 +#include +#include +#include +#include +#include + +#include "mdss_mdp.h" +#include "mdss_mdp_kcal_ctrl.h" + +static void kcal_apply_values(struct kcal_lut_data *lut_data) +{ + /* gc_lut_* will save lut values even when disabled and + * properly restore them on enable. + */ + lut_data->red = (lut_data->red < lut_data->minimum) ? + lut_data->minimum : lut_data->red; + lut_data->green = (lut_data->green < lut_data->minimum) ? + lut_data->minimum : lut_data->green; + lut_data->blue = (lut_data->blue < lut_data->minimum) ? + lut_data->minimum : lut_data->blue; + + mdss_mdp_pp_kcal_update(lut_data->red, lut_data->green, lut_data->blue); +} + +static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int kcal_r, kcal_g, kcal_b; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count > 12) + return -EINVAL; + + sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); + + if (kcal_r < 0 || kcal_r > 256) + return -EINVAL; + + if (kcal_g < 0 || kcal_g > 256) + return -EINVAL; + + if (kcal_b < 0 || kcal_b > 256) + return -EINVAL; + + lut_data->red = kcal_r; + lut_data->green = kcal_g; + lut_data->blue = kcal_b; + + kcal_apply_values(lut_data); + + return count; +} + +static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d %d %d\n", lut_data->red, lut_data->green, + lut_data->blue); +} + +static ssize_t kcal_min_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_min; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count > 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_min); + + if (kcal_min < 0 || kcal_min > 256) + return -EINVAL; + + lut_data->minimum = kcal_min; + + kcal_apply_values(lut_data); + + return count; +} + +static ssize_t kcal_min_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->minimum); +} + +static ssize_t kcal_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_enable; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count != 2) + return -EINVAL; + + sscanf(buf, "%d", &kcal_enable); + + if (kcal_enable != 0 && kcal_enable != 1) + return -EINVAL; + + if (lut_data->enable == kcal_enable) + return -EINVAL; + + lut_data->enable = kcal_enable; + + mdss_mdp_pp_kcal_enable(lut_data->enable ? true : false); + + return count; +} + +static ssize_t kcal_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->enable); +} + +static ssize_t kcal_invert_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_invert; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count != 2) + return -EINVAL; + + sscanf(buf, "%d", &kcal_invert); + + if (kcal_invert != 0 && kcal_invert != 1) + return -EINVAL; + + if (lut_data->invert == kcal_invert) + return -EINVAL; + + lut_data->invert = kcal_invert; + + mdss_mdp_pp_kcal_invert(lut_data->invert); + + return count; +} + +static ssize_t kcal_invert_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->invert); +} + +static ssize_t kcal_sat_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_sat; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count != 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_sat); + + if ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128) + return -EINVAL; + + lut_data->sat = kcal_sat; + + mdss_mdp_pp_kcal_pa(lut_data); + + return count; +} + +static ssize_t kcal_sat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->sat); +} + +static ssize_t kcal_hue_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_hue; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count > 5) + return -EINVAL; + + sscanf(buf, "%d", &kcal_hue); + + if (kcal_hue < 0 || kcal_hue > 1536) + return -EINVAL; + + lut_data->hue = kcal_hue; + + mdss_mdp_pp_kcal_pa(lut_data); + + return count; +} + +static ssize_t kcal_hue_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->hue); +} + +static ssize_t kcal_val_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_val; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count != 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_val); + + if (kcal_val < 128 || kcal_val > 383) + return -EINVAL; + + lut_data->val = kcal_val; + + mdss_mdp_pp_kcal_pa(lut_data); + + return count; +} + +static ssize_t kcal_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->val); +} + +static ssize_t kcal_cont_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int kcal_cont; + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (count != 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_cont); + + if (kcal_cont < 128 || kcal_cont > 383) + return -EINVAL; + + lut_data->cont = kcal_cont; + + mdss_mdp_pp_kcal_pa(lut_data); + + return count; +} + +static ssize_t kcal_cont_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lut_data->cont); +} + +static DEVICE_ATTR(kcal, 0644, kcal_show, kcal_store); +static DEVICE_ATTR(kcal_min, 0644, kcal_min_show, kcal_min_store); +static DEVICE_ATTR(kcal_enable, 0644, kcal_enable_show, kcal_enable_store); +static DEVICE_ATTR(kcal_invert, 0644, kcal_invert_show, kcal_invert_store); +static DEVICE_ATTR(kcal_sat, 0644, kcal_sat_show, kcal_sat_store); +static DEVICE_ATTR(kcal_hue, 0644, kcal_hue_show, kcal_hue_store); +static DEVICE_ATTR(kcal_val, 0644, kcal_val_show, kcal_val_store); +static DEVICE_ATTR(kcal_cont, 0644, kcal_cont_show, kcal_cont_store); + +static int __devinit kcal_ctrl_probe(struct platform_device *pdev) +{ + int ret; + struct kcal_lut_data *lut_data; + + lut_data = kzalloc(sizeof(*lut_data), GFP_KERNEL); + if (!lut_data) { + pr_err("%s: failed to allocate memory for lut_data\n", + __func__); + return -ENOMEM; + } + + mdss_mdp_pp_kcal_enable(true); + + mdss_mdp_pp_kcal_update(NUM_QLUT, NUM_QLUT, NUM_QLUT); + + lut_data->red = lut_data->green = lut_data->blue = NUM_QLUT; + lut_data->minimum = 35; + lut_data->enable = 1; + lut_data->invert = 0; + lut_data->sat = DEF_PA; + lut_data->hue = 0; + lut_data->val = DEF_PA; + lut_data->cont = DEF_PA; + + platform_set_drvdata(pdev, lut_data); + + ret = device_create_file(&pdev->dev, &dev_attr_kcal); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_min); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_enable); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_invert); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_sat); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_hue); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_val); + ret |= device_create_file(&pdev->dev, &dev_attr_kcal_cont); + if (ret) + pr_err("%s: unable to create sysfs entries\n", __func__); + + return ret; +} + +static int __devexit kcal_ctrl_remove(struct platform_device *pdev) +{ + struct kcal_lut_data *lut_data = platform_get_drvdata(pdev); + + device_remove_file(&pdev->dev, &dev_attr_kcal); + device_remove_file(&pdev->dev, &dev_attr_kcal_min); + device_remove_file(&pdev->dev, &dev_attr_kcal_enable); + device_remove_file(&pdev->dev, &dev_attr_kcal_invert); + device_remove_file(&pdev->dev, &dev_attr_kcal_sat); + device_remove_file(&pdev->dev, &dev_attr_kcal_hue); + device_remove_file(&pdev->dev, &dev_attr_kcal_val); + device_remove_file(&pdev->dev, &dev_attr_kcal_cont); + + kfree(lut_data); + + return 0; +} + +static struct platform_driver kcal_ctrl_driver = { + .probe = kcal_ctrl_probe, + .remove = kcal_ctrl_remove, + .driver = { + .name = "kcal_ctrl", + }, +}; + +static struct platform_device kcal_ctrl_device = { + .name = "kcal_ctrl", +}; + +static int __init kcal_ctrl_init(void) +{ + if (platform_driver_register(&kcal_ctrl_driver)) + return -ENODEV; + + if (platform_device_register(&kcal_ctrl_device)) + return -ENODEV; + + pr_info("%s: registered\n", __func__); + + return 0; +} + +static void __exit kcal_ctrl_exit(void) +{ + platform_device_unregister(&kcal_ctrl_device); + platform_driver_unregister(&kcal_ctrl_driver); +} + +late_initcall(kcal_ctrl_init); +module_exit(kcal_ctrl_exit); + +MODULE_DESCRIPTION("LCD KCAL Driver"); diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h new file mode 100644 index 000000000000..7aa229ef4b72 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h @@ -0,0 +1,45 @@ + +/* + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, LGE Inc. All rights reserved + * Copyright (c) 2014, savoca + * Copyright (c) 2014, Paul Reioux + * + * 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. + */ + +#ifndef __MDSS_MDP_KCAL_CTRL_H +#define __MDSS_MDP_KCAL_CTRL_H + +#define KCAL_DATA_R 0x01 +#define KCAL_DATA_G 0x02 +#define KCAL_DATA_B 0x03 + +#define NUM_QLUT 0x100 +#define DEF_PA 0xff + +struct kcal_lut_data { + int red; + int green; + int blue; + int minimum; + int enable; + int invert; + int sat; + int hue; + int val; + int cont; +}; + +void mdss_mdp_pp_kcal_enable(bool enable); +void mdss_mdp_pp_kcal_update(int kr, int kg, int kb); +void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data); +void mdss_mdp_pp_kcal_invert(int enable); +#endif diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index f434a437eeec..d8d278ffb8ed 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -22,6 +22,8 @@ #include #include +#include "mdss_mdp_kcal_ctrl.h" + struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = { [MDSS_MDP_CSC_RGB2RGB] = { 0, @@ -335,6 +337,33 @@ struct mdss_pp_res_type { struct pp_hist_col_info dspp_hist[MDSS_MDP_MAX_DSPP]; }; +uint32_t igc_Table_RGB[256] = { + 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, + 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, + 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, + 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, + 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, + 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, + 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, + 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, + 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, + 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, + 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, + 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, + 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, + 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, + 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, + 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, + 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, + 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, + 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, + 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, + 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, + 48, 32, 16, 0 +}; +int igc_c0_c1[256] = {0,}; +int igc_c2[256] = {0,}; + static DEFINE_MUTEX(mdss_pp_mutex); static struct mdss_pp_res_type *mdss_pp_res; @@ -426,6 +455,8 @@ static int pp_ad_linearize_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out, static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, bool *bl_out_notify); static int pp_ad_shutdown_cleanup(struct msm_fb_data_type *mfd); +static u32 pp_ad_attenuate_bl(u32 bl, struct mdss_ad_info *ad); +static struct msm_fb_data_type *mdss_get_mfd_from_index(int index); static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num); static inline bool pp_sts_is_enabled(u32 sts, int side); static inline void pp_sts_set_split_bits(u32 *sts, u32 bits); @@ -1865,6 +1896,10 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) mdss_pp_res->gamut_disp_cfg[disp_num].flags |= MDP_PP_OPS_WRITE; } + + if (!disp_num) + pp_sts.pgc_sts |= PP_STS_ENABLE; + if (pp_sts.pgc_sts & PP_STS_ENABLE) { flags |= PP_FLAGS_DIRTY_PGC; if (!(mdss_pp_res->pgc_disp_cfg[disp_num].flags @@ -1915,6 +1950,91 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } +void mdss_mdp_pp_kcal_enable(bool enable) +{ + u32 disp_num = 0, copyback = 0; + struct mdp_pgc_lut_data *pgc_config; + + pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + pgc_config->block = MDP_LOGICAL_BLOCK_DISP_0; + + if (enable) { + pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + pgc_config->r_data = &mdss_pp_res->gc_lut_r[disp_num][0]; + pgc_config->g_data = &mdss_pp_res->gc_lut_g[disp_num][0]; + pgc_config->b_data = &mdss_pp_res->gc_lut_b[disp_num][0]; + } else + pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + + mdss_mdp_argc_config(pgc_config, ©back); +} + +void mdss_mdp_pp_kcal_update(int kr, int kg, int kb) +{ + int i; + u32 disp_num = 0, copyback = 0; + struct mdp_pgc_lut_data *pgc_config; + + pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + + for (i = 0; i < GC_LUT_SEGMENTS; i++) { + pgc_config->r_data[i].slope = kr; + pgc_config->g_data[i].slope = kg; + pgc_config->b_data[i].slope = kb; + } + + mdss_mdp_argc_config(pgc_config, ©back); +} + +void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0; + struct mdp_pa_cfg_data pa_config; + + memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); + + pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + pa_config.pa_data.sat_adj = lut_data->sat; + pa_config.pa_data.hue_adj = lut_data->hue; + pa_config.pa_data.val_adj = lut_data->val; + pa_config.pa_data.cont_adj = lut_data->cont; + + mdss_mdp_pa_config(&pa_config, ©back); +} + +void mdss_mdp_pp_kcal_invert(int enable) +{ + int i; + u32 disp_num = 0, copyback = 0, copy_from_kernel = 1; + struct msm_fb_data_type *igc_mfd; + struct mdp_igc_lut_data *igc_config; + + igc_mfd = mdss_get_mfd_from_index(0); + + igc_config = &mdss_pp_res->igc_disp_cfg[disp_num]; + igc_config->c0_c1_data = &mdss_pp_res->igc_lut_c0c1[disp_num][0]; + igc_config->c2_data = &mdss_pp_res->igc_lut_c2[disp_num][0]; + igc_config->block = MDP_LOGICAL_BLOCK_DISP_0; + igc_config->len = IGC_LUT_ENTRIES; + + if (igc_mfd && enable) { + igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + for (i = 0; i < IGC_LUT_ENTRIES; i++) { + igc_c0_c1[i] = (igc_Table_RGB[i] & 0xfff) | + ((igc_Table_RGB[i] & 0xfff)) << 16; + igc_c2[i] = igc_Table_RGB[i]; + } + igc_config->c0_c1_data = &igc_c0_c1[0]; + igc_config->c2_data = &igc_c2[0]; + } else if (igc_mfd && !enable) + igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + else + return; + + mdss_mdp_igc_lut_config(igc_config, ©back, copy_from_kernel); +} + int mdss_mdp_pp_init(struct device *dev) { int i, ret = 0; From 037153438a3dbe364a974b8b46f799fbd5bd4b79 Mon Sep 17 00:00:00 2001 From: Tommy-Geenexus Date: Wed, 18 Feb 2015 09:53:09 +0100 Subject: [PATCH 015/118] msm: mdss: partly revert kcal interface to the old version (thanks to @savoca for the hint) --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 9 +- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h | 8 +- drivers/video/msm/mdss/mdss_mdp_pp.c | 146 +++++++++++++++++++- 3 files changed, 151 insertions(+), 12 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index e9da0516b3ad..f1f46504ba03 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -36,7 +36,7 @@ static void kcal_apply_values(struct kcal_lut_data *lut_data) lut_data->blue = (lut_data->blue < lut_data->minimum) ? lut_data->minimum : lut_data->blue; - mdss_mdp_pp_kcal_update(lut_data->red, lut_data->green, lut_data->blue); + update_preset_lcdc_lut(lut_data->red, lut_data->green, lut_data->blue); } static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, @@ -309,9 +309,10 @@ static int __devinit kcal_ctrl_probe(struct platform_device *pdev) mdss_mdp_pp_kcal_enable(true); - mdss_mdp_pp_kcal_update(NUM_QLUT, NUM_QLUT, NUM_QLUT); - - lut_data->red = lut_data->green = lut_data->blue = NUM_QLUT; + lut_data->red = mdss_mdp_pp_get_kcal(KCAL_DATA_R); + lut_data->green = mdss_mdp_pp_get_kcal(KCAL_DATA_G); + lut_data->blue = mdss_mdp_pp_get_kcal(KCAL_DATA_B); + lut_data->minimum = 35; lut_data->enable = 1; lut_data->invert = 0; diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h index 7aa229ef4b72..48f0e93ac6a4 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h @@ -23,8 +23,13 @@ #define KCAL_DATA_B 0x03 #define NUM_QLUT 0x100 +#define MAX_KCAL 256 #define DEF_PA 0xff +#define SCALED_BY_KCAL(rgb, kcal) \ + (((((unsigned int)(rgb) * (unsigned int)(kcal)) << 10) / \ + (unsigned int)MAX_KCAL) >> 10) + struct kcal_lut_data { int red; int green; @@ -39,7 +44,8 @@ struct kcal_lut_data { }; void mdss_mdp_pp_kcal_enable(bool enable); -void mdss_mdp_pp_kcal_update(int kr, int kg, int kb); +void update_preset_lcdc_lut(int kr, int kg, int kb); +int mdss_mdp_pp_get_kcal(int data); void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data); void mdss_mdp_pp_kcal_invert(int enable); #endif diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index d8d278ffb8ed..569cc2ff6872 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -1950,6 +1950,95 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } +static struct mdp_ar_gc_lut_data test_r[GC_LUT_SEGMENTS] = { + {0x00000000, 0x00000100, 0x00000000}, + {0x00000FFF, 0x00000000, 0x00007F80}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000} +}; + +static struct mdp_ar_gc_lut_data test_g[GC_LUT_SEGMENTS] = { + {0x00000000, 0x00000100, 0x00000000}, + {0x00000FFF, 0x00000000, 0x00007F80}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000} +}; + +static struct mdp_ar_gc_lut_data test_b[GC_LUT_SEGMENTS] = { + {0x00000000, 0x00000100, 0x00000000}, + {0x00000FFF, 0x00000000, 0x00007F80}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000} +}; + +static void mdss_mdp_pp_argc(void) +{ + int disp_num = 0; + u32 tbl_size; + + struct mdp_ar_gc_lut_data *r_data; + struct mdp_ar_gc_lut_data *g_data; + struct mdp_ar_gc_lut_data *b_data; + struct mdp_pgc_lut_data *pgc_config; + + r_data = &mdss_pp_res->gc_lut_r[disp_num][0]; + g_data = &mdss_pp_res->gc_lut_g[disp_num][0]; + b_data = &mdss_pp_res->gc_lut_b[disp_num][0]; + + tbl_size = GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data); + memcpy(r_data, test_r, tbl_size); + memcpy(g_data, test_g, tbl_size); + memcpy(b_data, test_b, tbl_size); + + pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + + pgc_config->r_data = + &mdss_pp_res->gc_lut_r[disp_num][0]; + pgc_config->g_data = + &mdss_pp_res->gc_lut_g[disp_num][0]; + pgc_config->b_data = + &mdss_pp_res->gc_lut_b[disp_num][0]; + + pgc_config->flags |= MDP_PP_OPS_WRITE; + pgc_config->flags |= MDP_PP_OPS_ENABLE; +} + void mdss_mdp_pp_kcal_enable(bool enable) { u32 disp_num = 0, copyback = 0; @@ -1969,21 +2058,58 @@ void mdss_mdp_pp_kcal_enable(bool enable) mdss_mdp_argc_config(pgc_config, ©back); } -void mdss_mdp_pp_kcal_update(int kr, int kg, int kb) +void update_preset_lcdc_lut(int kr, int kg, int kb) { int i; - u32 disp_num = 0, copyback = 0; + int disp_num = 0; struct mdp_pgc_lut_data *pgc_config; - pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + pr_info("r=[%d], g=[%d], b=[%d]\n", kr, kg, kb); for (i = 0; i < GC_LUT_SEGMENTS; i++) { - pgc_config->r_data[i].slope = kr; - pgc_config->g_data[i].slope = kg; - pgc_config->b_data[i].slope = kb; + mdss_pp_res->gc_lut_r[disp_num][i].slope = + SCALED_BY_KCAL(test_r[i].slope, kr); + mdss_pp_res->gc_lut_r[disp_num][i].offset = + SCALED_BY_KCAL(test_r[i].offset, kr); + + mdss_pp_res->gc_lut_g[disp_num][i].slope = + SCALED_BY_KCAL(test_g[i].slope, kg); + mdss_pp_res->gc_lut_g[disp_num][i].offset = + SCALED_BY_KCAL(test_g[i].offset, kg); + + mdss_pp_res->gc_lut_b[disp_num][i].slope = + SCALED_BY_KCAL(test_b[i].slope, kb); + mdss_pp_res->gc_lut_b[disp_num][i].offset = + SCALED_BY_KCAL(test_b[i].offset, kb); } - mdss_mdp_argc_config(pgc_config, ©back); + pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + pgc_config->flags |= MDP_PP_OPS_WRITE; + pgc_config->flags |= MDP_PP_OPS_ENABLE; + mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PGC; +} + +int mdss_mdp_pp_get_kcal(int data) +{ + int ret; + int disp_num = 0; + + switch (data) { + case KCAL_DATA_R: + ret = mdss_pp_res->gc_lut_r[disp_num][0].slope; + break; + case KCAL_DATA_G: + ret = mdss_pp_res->gc_lut_g[disp_num][0].slope; + break; + case KCAL_DATA_B: + ret = mdss_pp_res->gc_lut_b[disp_num][0].slope; + break; + default: + ret = 0; + break; + } + + return (ret == NUM_QLUT) ? MAX_KCAL : ret; } void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) @@ -2103,6 +2229,12 @@ int mdss_mdp_pp_init(struct device *dev) } } + + if (!ret) { + mdss_mdp_pp_argc(); + update_preset_lcdc_lut(MAX_KCAL, MAX_KCAL, MAX_KCAL); + } + mutex_unlock(&mdss_pp_mutex); return ret; } From 10fde724e4de1263d711d9107d0a7071fb27b63b Mon Sep 17 00:00:00 2001 From: naitikshah2015 Date: Sat, 23 May 2015 16:49:28 +0530 Subject: [PATCH 016/118] Updated KCAL driver with latest patches Conflicts: drivers/video/msm/mdss/mdss_mdp_pp.c Change-Id: Iaaf6b6392d42de3faf9e1e6c897047de07ff9ff6 Conflicts: drivers/video/msm/mdss/mdss_mdp_pp.c --- drivers/video/msm/mdss/Kconfig | 4 +- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 538 +++++++++++++++----- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h | 51 -- drivers/video/msm/mdss/mdss_mdp_pp.c | 250 --------- 4 files changed, 421 insertions(+), 422 deletions(-) delete mode 100644 drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig index 9a94946d297f..f70586e8dc10 100644 --- a/drivers/video/msm/mdss/Kconfig +++ b/drivers/video/msm/mdss/Kconfig @@ -58,5 +58,5 @@ config FB_MSM_MDSS_KCAL_CTRL depends on FB_MSM_MDSS bool "MDSS color control" ---help--- - Enable sysfs for rgb/lut control for mdss-mdp display controllers - in the MDSS sub-system. + Enable sysfs for post-processing control of mdss-mdp5 display + controllers in MDSS. diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index f1f46504ba03..2a8bab4f4a60 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -21,49 +21,282 @@ #include #include +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB) +#include +#include +#include +#elif defined(CONFIG_FB) +#include +#include +#endif + #include "mdss_mdp.h" -#include "mdss_mdp_kcal_ctrl.h" -static void kcal_apply_values(struct kcal_lut_data *lut_data) +#define DEF_PCC 0x100 +#define DEF_PA 0xff +#define PCC_ADJ 0x80 + +struct kcal_lut_data { +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB) + struct mmi_notifier panel_nb; +#elif defined(CONFIG_FB) + struct device dev; + struct notifier_block panel_nb; +#endif + bool queue_changes; + int red; + int green; + int blue; + int minimum; + int enable; + int invert; + int sat; + int hue; + int val; + int cont; +}; + +static uint32_t igc_inverted[IGC_LUT_ENTRIES] = { + 267390960, 266342368, 265293776, 264245184, + 263196592, 262148000, 261099408, 260050816, + 259002224, 257953632, 256905040, 255856448, + 254807856, 253759264, 252710672, 251662080, + 250613488, 249564896, 248516304, 247467712, + 246419120, 245370528, 244321936, 243273344, + 242224752, 241176160, 240127568, 239078976, + 238030384, 236981792, 235933200, 234884608, + 233836016, 232787424, 231738832, 230690240, + 229641648, 228593056, 227544464, 226495872, + 225447280, 224398688, 223350096, 222301504, + 221252912, 220204320, 219155728, 218107136, + 217058544, 216009952, 214961360, 213912768, + 212864176, 211815584, 210766992, 209718400, + 208669808, 207621216, 206572624, 205524032, + 204475440, 203426848, 202378256, 201329664, + 200281072, 199232480, 198183888, 197135296, + 196086704, 195038112, 193989520, 192940928, + 191892336, 190843744, 189795152, 188746560, + 187697968, 186649376, 185600784, 184552192, + 183503600, 182455008, 181406416, 180357824, + 179309232, 178260640, 177212048, 176163456, + 175114864, 174066272, 173017680, 171969088, + 170920496, 169871904, 168823312, 167774720, + 166726128, 165677536, 164628944, 163580352, + 162531760, 161483168, 160434576, 159385984, + 158337392, 157288800, 156240208, 155191616, + 154143024, 153094432, 152045840, 150997248, + 149948656, 148900064, 147851472, 146802880, + 145754288, 144705696, 143657104, 142608512, + 141559920, 140511328, 139462736, 138414144, + 137365552, 136316960, 135268368, 134219776, + 133171184, 132122592, 131074000, 130025408, + 128976816, 127928224, 126879632, 125831040, + 124782448, 123733856, 122685264, 121636672, + 120588080, 119539488, 118490896, 117442304, + 116393712, 115345120, 114296528, 113247936, + 112199344, 111150752, 110102160, 109053568, + 108004976, 106956384, 105907792, 104859200, + 103810608, 102762016, 101713424, 100664832, + 99616240, 98567648, 97519056, 96470464, + 95421872, 94373280, 93324688, 92276096, + 91227504, 90178912, 89130320, 88081728, + 87033136, 85984544, 84935952, 83887360, + 82838768, 81790176, 80741584, 79692992, + 78644400, 77595808, 76547216, 75498624, + 74450032, 73401440, 72352848, 71304256, + 70255664, 69207072, 68158480, 67109888, + 66061296, 65012704, 63964112, 62915520, + 61866928, 60818336, 59769744, 58721152, + 57672560, 56623968, 55575376, 54526784, + 53478192, 52429600, 51381008, 50332416, + 49283824, 48235232, 47186640, 46138048, + 45089456, 44040864, 42992272, 41943680, + 40895088, 39846496, 38797904, 37749312, + 36700720, 35652128, 34603536, 33554944, + 32506352, 31457760, 30409168, 29360576, + 28311984, 27263392, 26214800, 25166208, + 24117616, 23069024, 22020432, 20971840, + 19923248, 18874656, 17826064, 16777472, + 15728880, 14680288, 13631696, 12583104, + 11534512, 10485920, 9437328, 8388736, + 7340144, 6291552, 5242960, 4194368, + 3145776, 2097184, 1048592, 0 +}; + +static uint32_t igc_rgb[IGC_LUT_ENTRIES] = { + 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, + 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, + 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, + 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, + 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, + 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, + 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, + 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, + 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, + 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, + 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, + 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, + 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, + 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, + 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, + 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, + 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, + 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, + 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, + 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, + 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, + 48, 32, 16, 0 +}; + +static bool mdss_mdp_kcal_is_panel_on(void) +{ + int i; + struct mdss_mdp_ctl *ctl; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + for (i = 0; i < mdata->nctl; i++) { + ctl = mdata->ctl_off + i; + if (ctl->power_on) + return true; + } + + return false; +} + +static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) { - /* gc_lut_* will save lut values even when disabled and - * properly restore them on enable. + u32 copyback = 0; + struct mdp_pcc_cfg_data pcc_config; + + memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); + + pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pcc_config.ops = lut_data->enable ? + MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pcc_config.r.r = lut_data->red * PCC_ADJ; + pcc_config.g.g = lut_data->green * PCC_ADJ; + pcc_config.b.b = lut_data->blue * PCC_ADJ; + + mdss_mdp_pcc_config(&pcc_config, ©back); +} + +static void mdss_mdp_kcal_read_pcc(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0; + struct mdp_pcc_cfg_data pcc_config; + + memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); + + pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pcc_config.ops = MDP_PP_OPS_READ; + + mdss_mdp_pcc_config(&pcc_config, ©back); + + /* LiveDisplay disables pcc when using default values and regs + * are zeroed on pp resume, so throw these values out. */ - lut_data->red = (lut_data->red < lut_data->minimum) ? + if (!pcc_config.r.r && !pcc_config.g.g && !pcc_config.b.b) + return; + + lut_data->red = pcc_config.r.r / PCC_ADJ; + lut_data->green = pcc_config.g.g / PCC_ADJ; + lut_data->blue = pcc_config.b.b / PCC_ADJ; +} + +static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0; + struct mdp_pa_cfg_data pa_config; + struct mdp_pa_v2_cfg_data pa_v2_config; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + if (mdata->mdp_rev < MDSS_MDP_HW_REV_103) { + memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); + + pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_config.pa_data.flags = lut_data->enable ? + MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pa_config.pa_data.hue_adj = lut_data->hue; + pa_config.pa_data.sat_adj = lut_data->sat; + pa_config.pa_data.val_adj = lut_data->val; + pa_config.pa_data.cont_adj = lut_data->cont; + + mdss_mdp_pa_config(&pa_config, ©back); + } else { + memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); + + pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_v2_config.pa_v2_data.flags = lut_data->enable ? + MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_MASK; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_MASK; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_MASK; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_MASK; + pa_v2_config.pa_v2_data.global_hue_adj = lut_data->hue; + pa_v2_config.pa_v2_data.global_sat_adj = lut_data->sat; + pa_v2_config.pa_v2_data.global_val_adj = lut_data->val; + pa_v2_config.pa_v2_data.global_cont_adj = lut_data->cont; + + mdss_mdp_pa_v2_config(&pa_v2_config, ©back); + } +} + +static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0, copy_from_kernel = 1; + struct mdp_igc_lut_data igc_config; + + memset(&igc_config, 0, sizeof(struct mdp_igc_lut_data)); + + igc_config.block = MDP_LOGICAL_BLOCK_DISP_0; + igc_config.ops = lut_data->invert && lut_data->enable ? + MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + igc_config.len = IGC_LUT_ENTRIES; + igc_config.c0_c1_data = igc_inverted; + igc_config.c2_data = igc_rgb; + + mdss_mdp_igc_lut_config(&igc_config, ©back, copy_from_kernel); +} + +static void mdss_mdp_kcal_check_pcc(struct kcal_lut_data *lut_data) +{ + lut_data->red = lut_data->red < lut_data->minimum ? lut_data->minimum : lut_data->red; - lut_data->green = (lut_data->green < lut_data->minimum) ? + lut_data->green = lut_data->green < lut_data->minimum ? lut_data->minimum : lut_data->green; - lut_data->blue = (lut_data->blue < lut_data->minimum) ? + lut_data->blue = lut_data->blue < lut_data->minimum ? lut_data->minimum : lut_data->blue; - - update_preset_lcdc_lut(lut_data->red, lut_data->green, lut_data->blue); } static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_r, kcal_g, kcal_b; + int kcal_r, kcal_g, kcal_b, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count > 12) - return -EINVAL; - - sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); - - if (kcal_r < 0 || kcal_r > 256) - return -EINVAL; - - if (kcal_g < 0 || kcal_g > 256) - return -EINVAL; - - if (kcal_b < 0 || kcal_b > 256) + r = sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); + if ((r != 3) || (kcal_r < 1 || kcal_r > 256) || + (kcal_g < 1 || kcal_g > 256) || (kcal_b < 1 || kcal_b > 256)) return -EINVAL; lut_data->red = kcal_r; lut_data->green = kcal_g; lut_data->blue = kcal_b; - kcal_apply_values(lut_data); + mdss_mdp_kcal_check_pcc(lut_data); + + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pcc(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -73,27 +306,31 @@ static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d %d %d\n", lut_data->red, lut_data->green, - lut_data->blue); + if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) + mdss_mdp_kcal_read_pcc(lut_data); + + return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", + lut_data->red, lut_data->green, lut_data->blue); } static ssize_t kcal_min_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_min; + int kcal_min, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count > 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_min); - - if (kcal_min < 0 || kcal_min > 256) + r = kstrtoint(buf, 10, &kcal_min); + if ((r) || (kcal_min < 1 || kcal_min > 256)) return -EINVAL; lut_data->minimum = kcal_min; - kcal_apply_values(lut_data); + mdss_mdp_kcal_check_pcc(lut_data); + + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pcc(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -103,29 +340,28 @@ static ssize_t kcal_min_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->minimum); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->minimum); } static ssize_t kcal_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_enable; + int kcal_enable, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count != 2) - return -EINVAL; - - sscanf(buf, "%d", &kcal_enable); - - if (kcal_enable != 0 && kcal_enable != 1) - return -EINVAL; - - if (lut_data->enable == kcal_enable) + r = kstrtoint(buf, 10, &kcal_enable); + if ((r) || (kcal_enable != 0 && kcal_enable != 1) || + (lut_data->enable == kcal_enable)) return -EINVAL; lut_data->enable = kcal_enable; - mdss_mdp_pp_kcal_enable(lut_data->enable ? true : false); + if (mdss_mdp_kcal_is_panel_on()) { + mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); + } else + lut_data->queue_changes = true; return count; } @@ -135,29 +371,26 @@ static ssize_t kcal_enable_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->enable); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->enable); } static ssize_t kcal_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_invert; + int kcal_invert, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count != 2) - return -EINVAL; - - sscanf(buf, "%d", &kcal_invert); - - if (kcal_invert != 0 && kcal_invert != 1) - return -EINVAL; - - if (lut_data->invert == kcal_invert) + r = kstrtoint(buf, 10, &kcal_invert); + if ((r) || (kcal_invert != 0 && kcal_invert != 1) || + (lut_data->invert == kcal_invert)) return -EINVAL; lut_data->invert = kcal_invert; - mdss_mdp_pp_kcal_invert(lut_data->invert); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_igc(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -167,26 +400,27 @@ static ssize_t kcal_invert_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->invert); + /* IGC lut does not support reading regs in kernel space yet */ + + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->invert); } static ssize_t kcal_sat_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_sat; + int kcal_sat, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count != 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_sat); - - if ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128) + r = kstrtoint(buf, 10, &kcal_sat); + if ((r) || ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128)) return -EINVAL; lut_data->sat = kcal_sat; - mdss_mdp_pp_kcal_pa(lut_data); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -196,26 +430,25 @@ static ssize_t kcal_sat_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->sat); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->sat); } static ssize_t kcal_hue_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_hue; + int kcal_hue, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count > 5) - return -EINVAL; - - sscanf(buf, "%d", &kcal_hue); - - if (kcal_hue < 0 || kcal_hue > 1536) + r = kstrtoint(buf, 10, &kcal_hue); + if ((r) || (kcal_hue < 0 || kcal_hue > 1536)) return -EINVAL; lut_data->hue = kcal_hue; - mdss_mdp_pp_kcal_pa(lut_data); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -225,26 +458,25 @@ static ssize_t kcal_hue_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->hue); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->hue); } static ssize_t kcal_val_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_val; + int kcal_val, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count != 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_val); - - if (kcal_val < 128 || kcal_val > 383) + r = kstrtoint(buf, 10, &kcal_val); + if ((r) || (kcal_val < 128 || kcal_val > 383)) return -EINVAL; lut_data->val = kcal_val; - mdss_mdp_pp_kcal_pa(lut_data); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -254,26 +486,25 @@ static ssize_t kcal_val_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->val); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->val); } static ssize_t kcal_cont_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_cont; + int kcal_cont, r; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (count != 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_cont); - - if (kcal_cont < 128 || kcal_cont > 383) + r = kstrtoint(buf, 10, &kcal_cont); + if ((r) || (kcal_cont < 128 || kcal_cont > 383)) return -EINVAL; lut_data->cont = kcal_cont; - mdss_mdp_pp_kcal_pa(lut_data); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -283,45 +514,102 @@ static ssize_t kcal_cont_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", lut_data->cont); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->cont); +} + +static DEVICE_ATTR(kcal, S_IWUSR | S_IRUGO, kcal_show, kcal_store); +static DEVICE_ATTR(kcal_min, S_IWUSR | S_IRUGO, kcal_min_show, kcal_min_store); +static DEVICE_ATTR(kcal_enable, S_IWUSR | S_IRUGO, kcal_enable_show, + kcal_enable_store); +static DEVICE_ATTR(kcal_invert, S_IWUSR | S_IRUGO, kcal_invert_show, + kcal_invert_store); +static DEVICE_ATTR(kcal_sat, S_IWUSR | S_IRUGO, kcal_sat_show, kcal_sat_store); +static DEVICE_ATTR(kcal_hue, S_IWUSR | S_IRUGO, kcal_hue_show, kcal_hue_store); +static DEVICE_ATTR(kcal_val, S_IWUSR | S_IRUGO, kcal_val_show, kcal_val_store); +static DEVICE_ATTR(kcal_cont, S_IWUSR | S_IRUGO, kcal_cont_show, + kcal_cont_store); + +static int mdss_mdp_kcal_update_queue(struct device *dev) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (lut_data->queue_changes) { + mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); + lut_data->queue_changes = false; + } + + return 0; } -static DEVICE_ATTR(kcal, 0644, kcal_show, kcal_store); -static DEVICE_ATTR(kcal_min, 0644, kcal_min_show, kcal_min_store); -static DEVICE_ATTR(kcal_enable, 0644, kcal_enable_show, kcal_enable_store); -static DEVICE_ATTR(kcal_invert, 0644, kcal_invert_show, kcal_invert_store); -static DEVICE_ATTR(kcal_sat, 0644, kcal_sat_show, kcal_sat_store); -static DEVICE_ATTR(kcal_hue, 0644, kcal_hue_show, kcal_hue_store); -static DEVICE_ATTR(kcal_val, 0644, kcal_val_show, kcal_val_store); -static DEVICE_ATTR(kcal_cont, 0644, kcal_cont_show, kcal_cont_store); +#if defined(CONFIG_FB) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + struct kcal_lut_data *lut_data = + container_of(nb, struct kcal_lut_data, panel_nb); + + if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + mdss_mdp_kcal_update_queue(&lut_data->dev); + } + + return 0; +} +#endif -static int __devinit kcal_ctrl_probe(struct platform_device *pdev) +static int kcal_ctrl_probe(struct platform_device *pdev) { int ret; struct kcal_lut_data *lut_data; - lut_data = kzalloc(sizeof(*lut_data), GFP_KERNEL); + lut_data = devm_kzalloc(&pdev->dev, sizeof(*lut_data), GFP_KERNEL); if (!lut_data) { pr_err("%s: failed to allocate memory for lut_data\n", __func__); return -ENOMEM; } - mdss_mdp_pp_kcal_enable(true); + platform_set_drvdata(pdev, lut_data); - lut_data->red = mdss_mdp_pp_get_kcal(KCAL_DATA_R); - lut_data->green = mdss_mdp_pp_get_kcal(KCAL_DATA_G); - lut_data->blue = mdss_mdp_pp_get_kcal(KCAL_DATA_B); - - lut_data->minimum = 35; - lut_data->enable = 1; - lut_data->invert = 0; + lut_data->enable = 0x1; + lut_data->red = DEF_PCC; + lut_data->green = DEF_PCC; + lut_data->blue = DEF_PCC; + lut_data->minimum = 0x23; + lut_data->invert = 0x0; + lut_data->hue = 0x0; lut_data->sat = DEF_PA; - lut_data->hue = 0; lut_data->val = DEF_PA; lut_data->cont = DEF_PA; - platform_set_drvdata(pdev, lut_data); + lut_data->queue_changes = false; + + mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + lut_data->panel_nb.display_on = mdss_mdp_kcal_update_queue; + lut_data->panel_nb.dev = &pdev->dev; + ret = mmi_panel_register_notifier(&lut_data->panel_nb); + if (ret) { + pr_err("%s: unable to register MMI notifier\n", __func__); + return ret; + } +#elif defined(CONFIG_FB) + lut_data->dev = pdev->dev; + lut_data->panel_nb.notifier_call = fb_notifier_callback; + ret = fb_register_client(&lut_data->panel_nb); + if (ret) { + pr_err("%s: unable to register fb notifier\n", __func__); + return ret; + } +#endif ret = device_create_file(&pdev->dev, &dev_attr_kcal); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_min); @@ -331,13 +619,23 @@ static int __devinit kcal_ctrl_probe(struct platform_device *pdev) ret |= device_create_file(&pdev->dev, &dev_attr_kcal_hue); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_val); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_cont); - if (ret) + if (ret) { pr_err("%s: unable to create sysfs entries\n", __func__); + goto out_notifier; + } + return 0; + +out_notifier: +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&lut_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&lut_data->panel_nb); +#endif return ret; } -static int __devexit kcal_ctrl_remove(struct platform_device *pdev) +static int kcal_ctrl_remove(struct platform_device *pdev) { struct kcal_lut_data *lut_data = platform_get_drvdata(pdev); @@ -350,7 +648,11 @@ static int __devexit kcal_ctrl_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_kcal_val); device_remove_file(&pdev->dev, &dev_attr_kcal_cont); - kfree(lut_data); +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&lut_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&lut_data->panel_nb); +#endif return 0; } @@ -388,5 +690,3 @@ static void __exit kcal_ctrl_exit(void) late_initcall(kcal_ctrl_init); module_exit(kcal_ctrl_exit); - -MODULE_DESCRIPTION("LCD KCAL Driver"); diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h deleted file mode 100644 index 48f0e93ac6a4..000000000000 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h +++ /dev/null @@ -1,51 +0,0 @@ - -/* - * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. - * Copyright (c) 2013, LGE Inc. All rights reserved - * Copyright (c) 2014, savoca - * Copyright (c) 2014, Paul Reioux - * - * 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. - */ - -#ifndef __MDSS_MDP_KCAL_CTRL_H -#define __MDSS_MDP_KCAL_CTRL_H - -#define KCAL_DATA_R 0x01 -#define KCAL_DATA_G 0x02 -#define KCAL_DATA_B 0x03 - -#define NUM_QLUT 0x100 -#define MAX_KCAL 256 -#define DEF_PA 0xff - -#define SCALED_BY_KCAL(rgb, kcal) \ - (((((unsigned int)(rgb) * (unsigned int)(kcal)) << 10) / \ - (unsigned int)MAX_KCAL) >> 10) - -struct kcal_lut_data { - int red; - int green; - int blue; - int minimum; - int enable; - int invert; - int sat; - int hue; - int val; - int cont; -}; - -void mdss_mdp_pp_kcal_enable(bool enable); -void update_preset_lcdc_lut(int kr, int kg, int kb); -int mdss_mdp_pp_get_kcal(int data); -void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data); -void mdss_mdp_pp_kcal_invert(int enable); -#endif diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index 569cc2ff6872..a10daf6b901b 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -22,8 +22,6 @@ #include #include -#include "mdss_mdp_kcal_ctrl.h" - struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = { [MDSS_MDP_CSC_RGB2RGB] = { 0, @@ -337,33 +335,6 @@ struct mdss_pp_res_type { struct pp_hist_col_info dspp_hist[MDSS_MDP_MAX_DSPP]; }; -uint32_t igc_Table_RGB[256] = { - 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, - 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, - 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, - 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, - 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, - 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, - 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, - 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, - 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, - 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, - 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, - 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, - 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, - 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, - 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, - 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, - 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, - 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, - 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, - 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, - 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, - 48, 32, 16, 0 -}; -int igc_c0_c1[256] = {0,}; -int igc_c2[256] = {0,}; - static DEFINE_MUTEX(mdss_pp_mutex); static struct mdss_pp_res_type *mdss_pp_res; @@ -1896,10 +1867,6 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) mdss_pp_res->gamut_disp_cfg[disp_num].flags |= MDP_PP_OPS_WRITE; } - - if (!disp_num) - pp_sts.pgc_sts |= PP_STS_ENABLE; - if (pp_sts.pgc_sts & PP_STS_ENABLE) { flags |= PP_FLAGS_DIRTY_PGC; if (!(mdss_pp_res->pgc_disp_cfg[disp_num].flags @@ -1950,217 +1917,6 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } -static struct mdp_ar_gc_lut_data test_r[GC_LUT_SEGMENTS] = { - {0x00000000, 0x00000100, 0x00000000}, - {0x00000FFF, 0x00000000, 0x00007F80}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000} -}; - -static struct mdp_ar_gc_lut_data test_g[GC_LUT_SEGMENTS] = { - {0x00000000, 0x00000100, 0x00000000}, - {0x00000FFF, 0x00000000, 0x00007F80}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000} -}; - -static struct mdp_ar_gc_lut_data test_b[GC_LUT_SEGMENTS] = { - {0x00000000, 0x00000100, 0x00000000}, - {0x00000FFF, 0x00000000, 0x00007F80}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, 0x00000000} -}; - -static void mdss_mdp_pp_argc(void) -{ - int disp_num = 0; - u32 tbl_size; - - struct mdp_ar_gc_lut_data *r_data; - struct mdp_ar_gc_lut_data *g_data; - struct mdp_ar_gc_lut_data *b_data; - struct mdp_pgc_lut_data *pgc_config; - - r_data = &mdss_pp_res->gc_lut_r[disp_num][0]; - g_data = &mdss_pp_res->gc_lut_g[disp_num][0]; - b_data = &mdss_pp_res->gc_lut_b[disp_num][0]; - - tbl_size = GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data); - memcpy(r_data, test_r, tbl_size); - memcpy(g_data, test_g, tbl_size); - memcpy(b_data, test_b, tbl_size); - - pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; - - pgc_config->r_data = - &mdss_pp_res->gc_lut_r[disp_num][0]; - pgc_config->g_data = - &mdss_pp_res->gc_lut_g[disp_num][0]; - pgc_config->b_data = - &mdss_pp_res->gc_lut_b[disp_num][0]; - - pgc_config->flags |= MDP_PP_OPS_WRITE; - pgc_config->flags |= MDP_PP_OPS_ENABLE; -} - -void mdss_mdp_pp_kcal_enable(bool enable) -{ - u32 disp_num = 0, copyback = 0; - struct mdp_pgc_lut_data *pgc_config; - - pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; - pgc_config->block = MDP_LOGICAL_BLOCK_DISP_0; - - if (enable) { - pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pgc_config->r_data = &mdss_pp_res->gc_lut_r[disp_num][0]; - pgc_config->g_data = &mdss_pp_res->gc_lut_g[disp_num][0]; - pgc_config->b_data = &mdss_pp_res->gc_lut_b[disp_num][0]; - } else - pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - - mdss_mdp_argc_config(pgc_config, ©back); -} - -void update_preset_lcdc_lut(int kr, int kg, int kb) -{ - int i; - int disp_num = 0; - struct mdp_pgc_lut_data *pgc_config; - - pr_info("r=[%d], g=[%d], b=[%d]\n", kr, kg, kb); - - for (i = 0; i < GC_LUT_SEGMENTS; i++) { - mdss_pp_res->gc_lut_r[disp_num][i].slope = - SCALED_BY_KCAL(test_r[i].slope, kr); - mdss_pp_res->gc_lut_r[disp_num][i].offset = - SCALED_BY_KCAL(test_r[i].offset, kr); - - mdss_pp_res->gc_lut_g[disp_num][i].slope = - SCALED_BY_KCAL(test_g[i].slope, kg); - mdss_pp_res->gc_lut_g[disp_num][i].offset = - SCALED_BY_KCAL(test_g[i].offset, kg); - - mdss_pp_res->gc_lut_b[disp_num][i].slope = - SCALED_BY_KCAL(test_b[i].slope, kb); - mdss_pp_res->gc_lut_b[disp_num][i].offset = - SCALED_BY_KCAL(test_b[i].offset, kb); - } - - pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; - pgc_config->flags |= MDP_PP_OPS_WRITE; - pgc_config->flags |= MDP_PP_OPS_ENABLE; - mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PGC; -} - -int mdss_mdp_pp_get_kcal(int data) -{ - int ret; - int disp_num = 0; - - switch (data) { - case KCAL_DATA_R: - ret = mdss_pp_res->gc_lut_r[disp_num][0].slope; - break; - case KCAL_DATA_G: - ret = mdss_pp_res->gc_lut_g[disp_num][0].slope; - break; - case KCAL_DATA_B: - ret = mdss_pp_res->gc_lut_b[disp_num][0].slope; - break; - default: - ret = 0; - break; - } - - return (ret == NUM_QLUT) ? MAX_KCAL : ret; -} - -void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0; - struct mdp_pa_cfg_data pa_config; - - memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); - - pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pa_config.pa_data.sat_adj = lut_data->sat; - pa_config.pa_data.hue_adj = lut_data->hue; - pa_config.pa_data.val_adj = lut_data->val; - pa_config.pa_data.cont_adj = lut_data->cont; - - mdss_mdp_pa_config(&pa_config, ©back); -} - -void mdss_mdp_pp_kcal_invert(int enable) -{ - int i; - u32 disp_num = 0, copyback = 0, copy_from_kernel = 1; - struct msm_fb_data_type *igc_mfd; - struct mdp_igc_lut_data *igc_config; - - igc_mfd = mdss_get_mfd_from_index(0); - - igc_config = &mdss_pp_res->igc_disp_cfg[disp_num]; - igc_config->c0_c1_data = &mdss_pp_res->igc_lut_c0c1[disp_num][0]; - igc_config->c2_data = &mdss_pp_res->igc_lut_c2[disp_num][0]; - igc_config->block = MDP_LOGICAL_BLOCK_DISP_0; - igc_config->len = IGC_LUT_ENTRIES; - - if (igc_mfd && enable) { - igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - for (i = 0; i < IGC_LUT_ENTRIES; i++) { - igc_c0_c1[i] = (igc_Table_RGB[i] & 0xfff) | - ((igc_Table_RGB[i] & 0xfff)) << 16; - igc_c2[i] = igc_Table_RGB[i]; - } - igc_config->c0_c1_data = &igc_c0_c1[0]; - igc_config->c2_data = &igc_c2[0]; - } else if (igc_mfd && !enable) - igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - else - return; - - mdss_mdp_igc_lut_config(igc_config, ©back, copy_from_kernel); -} - int mdss_mdp_pp_init(struct device *dev) { int i, ret = 0; @@ -2229,12 +1985,6 @@ int mdss_mdp_pp_init(struct device *dev) } } - - if (!ret) { - mdss_mdp_pp_argc(); - update_preset_lcdc_lut(MAX_KCAL, MAX_KCAL, MAX_KCAL); - } - mutex_unlock(&mdss_pp_mutex); return ret; } From ef1aeb100c00f49fb420178af49b6533cf33c123 Mon Sep 17 00:00:00 2001 From: Saatvik Shukla Date: Sat, 25 Jul 2015 21:19:16 +0530 Subject: [PATCH 017/118] Enable Kcal and Sound Control from defconfig Change-Id: I58189840163ca38f890954c57b247cf41ec22c13 --- arch/arm/configs/cm_rhine_amami_row_defconfig | 4 ++++ arch/arm/configs/cm_rhine_honami_row_defconfig | 4 ++++ arch/arm/configs/cm_rhine_togari_row_defconfig | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/arch/arm/configs/cm_rhine_amami_row_defconfig b/arch/arm/configs/cm_rhine_amami_row_defconfig index b411294538c9..793172874f9a 100644 --- a/arch/arm/configs/cm_rhine_amami_row_defconfig +++ b/arch/arm/configs/cm_rhine_amami_row_defconfig @@ -612,3 +612,7 @@ CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_DEV_QCE=y CONFIG_CRYPTO_DEV_QCEDEV=y + +#BlackSheep +CONFIG_FB_MSM_MDSS_KCAL_CTRL=y +CONFIG_SOUND_CONTROL_HAX_3_GPL=y diff --git a/arch/arm/configs/cm_rhine_honami_row_defconfig b/arch/arm/configs/cm_rhine_honami_row_defconfig index 7ea6614c2f38..1cb879f62f88 100644 --- a/arch/arm/configs/cm_rhine_honami_row_defconfig +++ b/arch/arm/configs/cm_rhine_honami_row_defconfig @@ -612,3 +612,7 @@ CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_DEV_QCE=y CONFIG_CRYPTO_DEV_QCEDEV=y + +#BlackSheep +CONFIG_FB_MSM_MDSS_KCAL_CTRL=y +CONFIG_SOUND_CONTROL_HAX_3_GPL=y diff --git a/arch/arm/configs/cm_rhine_togari_row_defconfig b/arch/arm/configs/cm_rhine_togari_row_defconfig index ebbe05cef891..3c6738b17d95 100644 --- a/arch/arm/configs/cm_rhine_togari_row_defconfig +++ b/arch/arm/configs/cm_rhine_togari_row_defconfig @@ -610,3 +610,7 @@ CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_DEV_QCE=y CONFIG_CRYPTO_DEV_QCEDEV=y + +#BlackSheep +CONFIG_FB_MSM_MDSS_KCAL_CTRL=y +CONFIG_SOUND_CONTROL_HAX_3_GPL=y From fcfb8924c54ea2959037fa8b51d91f58772fc779 Mon Sep 17 00:00:00 2001 From: Saatvik Shukla Date: Wed, 30 Sep 2015 14:35:45 +0530 Subject: [PATCH 018/118] KCAL : remove duplicate declaration of pp_ad_attenuate_bl Change-Id: I702935e2d379099eef3ff8afd4be5072b7412519 Signed-off-by: Saatvik Shukla --- drivers/video/msm/mdss/mdss_mdp_pp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index a10daf6b901b..911af8e3b1a5 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -426,7 +426,6 @@ static int pp_ad_linearize_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out, static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, bool *bl_out_notify); static int pp_ad_shutdown_cleanup(struct msm_fb_data_type *mfd); -static u32 pp_ad_attenuate_bl(u32 bl, struct mdss_ad_info *ad); static struct msm_fb_data_type *mdss_get_mfd_from_index(int index); static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num); static inline bool pp_sts_is_enabled(u32 sts, int side); From 10e2c8600316260d6abb3f56956b262d12cb52f2 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Sun, 7 Sep 2014 13:34:18 -0700 Subject: [PATCH 019/118] intelli-thermal v2: initial adaptation Intellithermal 2.0 is based on the latest Qualcomm thermal driver adapted for in-kernel use and control. Newer MSM8974+ chipsets should use this driver going forward Signed-off-by: Paul Reioux Conflicts: drivers/thermal/Kconfig drivers/thermal/Makefile include/linux/msm_thermal.h Conflicts: include/linux/msm_thermal.h --- drivers/thermal/Kconfig | 12 + drivers/thermal/Makefile | 1 + drivers/thermal/intelli/msm_thermal_v2.c | 2789 ++++++++++++++++++++++ include/linux/msm_thermal.h | 5 +- 4 files changed, 2806 insertions(+), 1 deletion(-) create mode 100644 drivers/thermal/intelli/msm_thermal_v2.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 347d9f2c64af..793cb9ebda19 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -73,6 +73,18 @@ config THERMAL_PM8XXX shifting over temperature shutdown control of the PMIC from hardware to software. +choice + prompt "MSM Thermal Monitor Types" + default THERMAL_MONITOR + +config INTELLI_THERMAL_V2 + bool "Better thermal monitor for MSM" + depends on THERMAL_TSENS8974 + depends on CPU_FREQ_MSM + help + This enables thermal monitoring capability in the kernel replacing + userspace thermal monitoring altogether. + config THERMAL_MONITOR bool "Monitor thermal state and limit CPU Frequency" depends on THERMAL_TSENS8960 || THERMAL_TSENS8974 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 0d17026c539e..d7d56c205500 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_THERMAL_MSM_POPMEM) += msm_popmem-tm.o obj-$(CONFIG_THERMAL_TSENS) += msm_tsens.o obj-$(CONFIG_THERMAL_TSENS8960) += msm8960_tsens.o obj-$(CONFIG_THERMAL_PM8XXX) += pm8xxx-tm.o +obj-$(CONFIG_INTELLI_THERMAL_V2) += intelli/msm_thermal_v2.o obj-$(CONFIG_THERMAL_MONITOR) += msm_thermal.o msm_thermal-dev.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_THERMAL_TSENS8974) += msm8974-tsens.o diff --git a/drivers/thermal/intelli/msm_thermal_v2.c b/drivers/thermal/intelli/msm_thermal_v2.c new file mode 100644 index 000000000000..af999cf71425 --- /dev/null +++ b/drivers/thermal/intelli/msm_thermal_v2.c @@ -0,0 +1,2789 @@ +/* Copyright (c) 2012-2014, The Linux Foundation. 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 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. + * + * Added code to work as a standalone intelligent thermal throttling driver + * for newer Qualcomm SOCs based on 8974 and 8226 by Paul Reioux (Faux123) + * Modifications copyright (c) 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CURRENT_UA 1000000 +#define MAX_RAILS 5 +#define MAX_THRESHOLD 2 + +static struct msm_thermal_data msm_thermal_info; +static struct delayed_work check_temp_work; +static bool core_control_enabled; +static uint32_t cpus_offlined; +static DEFINE_MUTEX(core_control_mutex); +static uint32_t wakeup_ms; +static struct alarm thermal_rtc; +static struct kobject *tt_kobj; +static struct kobject *cc_kobj; +static struct work_struct timer_work; +static struct task_struct *hotplug_task; +static struct task_struct *freq_mitigation_task; +static struct completion hotplug_notify_complete; +static struct completion freq_mitigation_complete; + +static int enabled; +static int rails_cnt; +static int psm_rails_cnt; +static int ocr_rail_cnt; +static int limit_idx; +static int limit_idx_low; +static int limit_idx_high; +static int max_tsens_num; +static struct cpufreq_frequency_table *table; +static uint32_t usefreq; +static int freq_table_get; +static bool vdd_rstr_enabled; +static bool vdd_rstr_nodes_called; +static bool vdd_rstr_probed; +static bool psm_enabled; +static bool psm_nodes_called; +static bool psm_probed; +static bool hotplug_enabled; +static bool freq_mitigation_enabled; +static bool ocr_enabled; +static bool ocr_nodes_called; +static bool ocr_probed; +static int *tsens_id_map; +static DEFINE_MUTEX(vdd_rstr_mutex); +static DEFINE_MUTEX(psm_mutex); +static DEFINE_MUTEX(ocr_mutex); +static uint32_t min_freq_limit; + +enum thermal_threshold { + HOTPLUG_THRESHOLD_HIGH, + HOTPLUG_THRESHOLD_LOW, + FREQ_THRESHOLD_HIGH, + FREQ_THRESHOLD_LOW, + THRESHOLD_MAX_NR, +}; + +enum sensor_id_type { + THERM_ZONE_ID, + THERM_TSENS_ID, + THERM_ID_MAX_NR, +}; + +struct cpu_info { + uint32_t cpu; + const char *sensor_type; + enum sensor_id_type id_type; + uint32_t sensor_id; + bool offline; + bool user_offline; + bool hotplug_thresh_clear; + struct sensor_threshold threshold[THRESHOLD_MAX_NR]; + bool max_freq; + uint32_t user_max_freq; + uint32_t user_min_freq; + uint32_t limited_max_freq; + uint32_t limited_min_freq; + bool freq_thresh_clear; +}; + +struct rail { + const char *name; + uint32_t freq_req; + uint32_t min_level; + uint32_t num_levels; + int32_t curr_level; + uint32_t levels[3]; + struct kobj_attribute value_attr; + struct kobj_attribute level_attr; + struct regulator *reg; + struct attribute_group attr_gp; +}; + +struct psm_rail { + const char *name; + uint8_t init; + uint8_t mode; + struct kobj_attribute mode_attr; + struct rpm_regulator *reg; + struct regulator *phase_reg; + struct attribute_group attr_gp; +}; + +static struct psm_rail *psm_rails; +static struct psm_rail *ocr_rails; +static struct rail *rails; +static struct cpu_info cpus[NR_CPUS]; + +struct vdd_rstr_enable { + struct kobj_attribute ko_attr; + uint32_t enabled; +}; + +/* For SMPS only*/ +enum PMIC_SW_MODE { + PMIC_AUTO_MODE = RPM_REGULATOR_MODE_AUTO, + PMIC_IPEAK_MODE = RPM_REGULATOR_MODE_IPEAK, + PMIC_PWM_MODE = RPM_REGULATOR_MODE_HPM, +}; + +enum ocr_request { + OPTIMUM_CURRENT_MIN, + OPTIMUM_CURRENT_MAX, + OPTIMUM_CURRENT_NR, +}; + +#define VDD_RES_RO_ATTRIB(_rail, ko_attr, j, _name) \ + ko_attr.attr.name = __stringify(_name); \ + ko_attr.attr.mode = 0444; \ + ko_attr.show = vdd_rstr_reg_##_name##_show; \ + ko_attr.store = NULL; \ + sysfs_attr_init(&ko_attr.attr); \ + _rail.attr_gp.attrs[j] = &ko_attr.attr; + +#define VDD_RES_RW_ATTRIB(_rail, ko_attr, j, _name) \ + ko_attr.attr.name = __stringify(_name); \ + ko_attr.attr.mode = 0644; \ + ko_attr.show = vdd_rstr_reg_##_name##_show; \ + ko_attr.store = vdd_rstr_reg_##_name##_store; \ + sysfs_attr_init(&ko_attr.attr); \ + _rail.attr_gp.attrs[j] = &ko_attr.attr; + +#define VDD_RSTR_ENABLE_FROM_ATTRIBS(attr) \ + (container_of(attr, struct vdd_rstr_enable, ko_attr)); + +#define VDD_RSTR_REG_VALUE_FROM_ATTRIBS(attr) \ + (container_of(attr, struct rail, value_attr)); + +#define VDD_RSTR_REG_LEVEL_FROM_ATTRIBS(attr) \ + (container_of(attr, struct rail, level_attr)); + +#define OCR_RW_ATTRIB(_rail, ko_attr, j, _name) \ + ko_attr.attr.name = __stringify(_name); \ + ko_attr.attr.mode = 0644; \ + ko_attr.show = ocr_reg_##_name##_show; \ + ko_attr.store = ocr_reg_##_name##_store; \ + sysfs_attr_init(&ko_attr.attr); \ + _rail.attr_gp.attrs[j] = &ko_attr.attr; + +#define PSM_RW_ATTRIB(_rail, ko_attr, j, _name) \ + ko_attr.attr.name = __stringify(_name); \ + ko_attr.attr.mode = 0644; \ + ko_attr.show = psm_reg_##_name##_show; \ + ko_attr.store = psm_reg_##_name##_store; \ + sysfs_attr_init(&ko_attr.attr); \ + _rail.attr_gp.attrs[j] = &ko_attr.attr; + +#define PSM_REG_MODE_FROM_ATTRIBS(attr) \ + (container_of(attr, struct psm_rail, mode_attr)); + +#define DEFAULT_POLLING_MS 250 +/* last 3 minutes based on 250ms polling cycle */ +#define MAX_HISTORY_SZ ((3*60*1000) / DEFAULT_POLLING_MS) + +struct msm_thermal_stat_data { + int32_t temp_history[MAX_HISTORY_SZ]; + uint32_t throttled; + uint32_t warning; + uint32_t normal; +}; +static struct msm_thermal_stat_data msm_thermal_stats; + +/* module parameters */ +module_param_named(poll_ms, msm_thermal_info.poll_ms, uint, 0664); +module_param_named(limit_temp_degC, msm_thermal_info.limit_temp_degC, + int, 0664); +module_param_named(freq_control_mask, msm_thermal_info.bootup_freq_control_mask, + uint, 0664); +module_param_named(core_limit_temp_degC, msm_thermal_info.core_limit_temp_degC, + int, 0664); +module_param_named(core_control_mask, msm_thermal_info.core_control_mask, + uint, 0664); + +static int msm_thermal_cpufreq_callback(struct notifier_block *nfb, + unsigned long event, void *data) +{ + struct cpufreq_policy *policy = data; + uint32_t max_freq_req = cpus[policy->cpu].limited_max_freq; + uint32_t min_freq_req = cpus[policy->cpu].limited_min_freq; + + switch (event) { + case CPUFREQ_INCOMPATIBLE: + pr_debug("%s: mitigating cpu %d to freq max: %u min: %u\n", + KBUILD_MODNAME, policy->cpu, max_freq_req, min_freq_req); + + cpufreq_verify_within_limits(policy, min_freq_req, + max_freq_req); + + if (max_freq_req < min_freq_req) + pr_err("Invalid frequency request Max:%u Min:%u\n", + max_freq_req, min_freq_req); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block msm_thermal_cpufreq_notifier = { + .notifier_call = msm_thermal_cpufreq_callback, +}; + +/* If freq table exists, then we can send freq request */ +static int check_freq_table(void) +{ + int ret = 0; + struct cpufreq_frequency_table *table = NULL; + + table = cpufreq_frequency_get_table(0); + if (!table) { + pr_debug("%s: error reading cpufreq table\n", __func__); + return -EINVAL; + } + freq_table_get = 1; + + return ret; +} + +static void update_cpu_freq(int cpu) +{ + if (cpu_online(cpu)) { + if (cpufreq_update_policy(cpu)) + pr_err("Unable to update policy for cpu:%d\n", cpu); + } +} + +static int update_cpu_min_freq_all(uint32_t min) +{ + uint32_t cpu = 0; + int ret = 0; + + if (!freq_table_get) { + ret = check_freq_table(); + if (ret) { + pr_err("%s:Fail to get freq table\n", KBUILD_MODNAME); + return ret; + } + } + /* If min is larger than allowed max */ + min = min(min, table[limit_idx_high].frequency); + + if (freq_mitigation_task) { + min_freq_limit = min; + complete(&freq_mitigation_complete); + } else { + get_online_cpus(); + for_each_possible_cpu(cpu) { + cpus[cpu].limited_min_freq = min; + update_cpu_freq(cpu); + } + put_online_cpus(); + } + + return ret; +} + +static int vdd_restriction_apply_freq(struct rail *r, int level) +{ + int ret = 0; + + if (level == r->curr_level) + return ret; + + /* level = -1: disable, level = 0,1,2..n: enable */ + if (level == -1) { + ret = update_cpu_min_freq_all(r->min_level); + if (ret) + return ret; + else + r->curr_level = -1; + } else if (level >= 0 && level < (r->num_levels)) { + ret = update_cpu_min_freq_all(r->levels[level]); + if (ret) + return ret; + else + r->curr_level = level; + } else { + pr_err("level input:%d is not within range\n", level); + return -EINVAL; + } + + return ret; +} + +static int vdd_restriction_apply_voltage(struct rail *r, int level) +{ + int ret = 0; + + if (r->reg == NULL) { + pr_info("Do not have regulator handle:%s, can't apply vdd\n", + r->name); + return -EFAULT; + } + if (level == r->curr_level) + return ret; + + /* level = -1: disable, level = 0,1,2..n: enable */ + if (level == -1) { + ret = regulator_set_voltage(r->reg, r->min_level, + r->levels[r->num_levels - 1]); + if (!ret) + r->curr_level = -1; + } else if (level >= 0 && level < (r->num_levels)) { + ret = regulator_set_voltage(r->reg, r->levels[level], + r->levels[r->num_levels - 1]); + if (!ret) + r->curr_level = level; + } else { + pr_err("level input:%d is not within range\n", level); + return -EINVAL; + } + + return ret; +} + +/* Setting all rails the same mode */ +static int psm_set_mode_all(int mode) +{ + int i = 0; + int fail_cnt = 0; + int ret = 0; + + for (i = 0; i < psm_rails_cnt; i++) { + if (psm_rails[i].mode != mode) { + ret = rpm_regulator_set_mode(psm_rails[i].reg, mode); + if (ret) { + pr_err("Cannot set mode:%d for %s", + mode, psm_rails[i].name); + fail_cnt++; + } else + psm_rails[i].mode = mode; + } + } + + return fail_cnt ? (-EFAULT) : ret; +} + +static int vdd_rstr_en_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct vdd_rstr_enable *en = VDD_RSTR_ENABLE_FROM_ATTRIBS(attr); + + return snprintf(buf, PAGE_SIZE, "%d\n", en->enabled); +} + +static ssize_t vdd_rstr_en_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + int i = 0; + uint8_t en_cnt = 0; + uint8_t dis_cnt = 0; + uint32_t val = 0; + struct kernel_param kp; + struct vdd_rstr_enable *en = VDD_RSTR_ENABLE_FROM_ATTRIBS(attr); + + mutex_lock(&vdd_rstr_mutex); + kp.arg = &val; + ret = param_set_bool(buf, &kp); + if (ret) { + pr_err("Invalid input %s for enabled\n", buf); + goto done_vdd_rstr_en; + } + + if ((val == 0) && (en->enabled == 0)) + goto done_vdd_rstr_en; + + for (i = 0; i < rails_cnt; i++) { + if (rails[i].freq_req == 1 && freq_table_get) + ret = vdd_restriction_apply_freq(&rails[i], + (val) ? 0 : -1); + else + ret = vdd_restriction_apply_voltage(&rails[i], + (val) ? 0 : -1); + + /* + * Even if fail to set one rail, still try to set the + * others. Continue the loop + */ + if (ret) + pr_err("Set vdd restriction for %s failed\n", + rails[i].name); + else { + if (val) + en_cnt++; + else + dis_cnt++; + } + } + /* As long as one rail is enabled, vdd rstr is enabled */ + if (val && en_cnt) + en->enabled = 1; + else if (!val && (dis_cnt == rails_cnt)) + en->enabled = 0; + +done_vdd_rstr_en: + mutex_unlock(&vdd_rstr_mutex); + return count; +} + +static struct vdd_rstr_enable vdd_rstr_en = { + .ko_attr.attr.name = __stringify(enabled), + .ko_attr.attr.mode = 0644, + .ko_attr.show = vdd_rstr_en_show, + .ko_attr.store = vdd_rstr_en_store, + .enabled = 1, +}; + +static struct attribute *vdd_rstr_en_attribs[] = { + &vdd_rstr_en.ko_attr.attr, + NULL, +}; + +static struct attribute_group vdd_rstr_en_attribs_gp = { + .attrs = vdd_rstr_en_attribs, +}; + +static int vdd_rstr_reg_value_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int val = 0; + struct rail *reg = VDD_RSTR_REG_VALUE_FROM_ATTRIBS(attr); + /* -1:disabled, -2:fail to get regualtor handle */ + if (reg->curr_level < 0) + val = reg->curr_level; + else + val = reg->levels[reg->curr_level]; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static int vdd_rstr_reg_level_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct rail *reg = VDD_RSTR_REG_LEVEL_FROM_ATTRIBS(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", reg->curr_level); +} + +static ssize_t vdd_rstr_reg_level_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + int val = 0; + + struct rail *reg = VDD_RSTR_REG_LEVEL_FROM_ATTRIBS(attr); + + mutex_lock(&vdd_rstr_mutex); + if (vdd_rstr_en.enabled == 0) + goto done_store_level; + + ret = kstrtouint(buf, 10, &val); + if (ret) { + pr_err("Invalid input %s for level\n", buf); + goto done_store_level; + } + + if (val < 0 || val > reg->num_levels - 1) { + pr_err(" Invalid number %d for level\n", val); + goto done_store_level; + } + + if (val != reg->curr_level) { + if (reg->freq_req == 1 && freq_table_get) + update_cpu_min_freq_all(reg->levels[val]); + else { + ret = vdd_restriction_apply_voltage(reg, val); + if (ret) { + pr_err( \ + "Set vdd restriction for regulator %s failed\n", + reg->name); + goto done_store_level; + } + } + reg->curr_level = val; + } + +done_store_level: + mutex_unlock(&vdd_rstr_mutex); + return count; +} + +static int request_optimum_current(struct psm_rail *rail, enum ocr_request req) +{ + int ret = 0; + + if ((!rail) || (req >= OPTIMUM_CURRENT_NR) || + (req < 0)) { + pr_err("%s:%s Invalid input\n", KBUILD_MODNAME, __func__); + ret = -EINVAL; + goto request_ocr_exit; + } + + ret = regulator_set_optimum_mode(rail->phase_reg, + (req == OPTIMUM_CURRENT_MAX) ? MAX_CURRENT_UA : 0); + if (ret < 0) { + pr_err("%s: Optimum current request failed\n", KBUILD_MODNAME); + goto request_ocr_exit; + } + ret = 0; /*regulator_set_optimum_mode returns the mode on success*/ + pr_debug("%s: Requested optimum current mode: %d\n", + KBUILD_MODNAME, req); + +request_ocr_exit: + return ret; +} + +static int ocr_set_mode_all(enum ocr_request req) +{ + int ret = 0, i; + + for (i = 0; i < ocr_rail_cnt; i++) { + if (ocr_rails[i].mode == req) + continue; + ret = request_optimum_current(&ocr_rails[i], req); + if (ret) + goto ocr_set_mode_exit; + ocr_rails[i].mode = req; + } + +ocr_set_mode_exit: + return ret; +} + +static int ocr_reg_mode_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct psm_rail *reg = PSM_REG_MODE_FROM_ATTRIBS(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", reg->mode); +} + +static ssize_t ocr_reg_mode_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + int val = 0; + struct psm_rail *reg = PSM_REG_MODE_FROM_ATTRIBS(attr); + + if (!ocr_enabled) + return count; + + mutex_lock(&ocr_mutex); + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_err("%s: Invalid input %s for mode\n", + KBUILD_MODNAME, buf); + goto done_ocr_store; + } + + if ((val != OPTIMUM_CURRENT_MAX) && + (val != OPTIMUM_CURRENT_MIN)) { + pr_err("%s: Invalid value %d for mode\n", + KBUILD_MODNAME, val); + goto done_ocr_store; + } + + if (val != reg->mode) { + ret = request_optimum_current(reg, val); + if (ret) + goto done_ocr_store; + reg->mode = val; + } + +done_ocr_store: + mutex_unlock(&ocr_mutex); + return count; +} + +static int psm_reg_mode_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct psm_rail *reg = PSM_REG_MODE_FROM_ATTRIBS(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", reg->mode); +} + +static ssize_t psm_reg_mode_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + int val = 0; + struct psm_rail *reg = PSM_REG_MODE_FROM_ATTRIBS(attr); + + mutex_lock(&psm_mutex); + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_err("Invalid input %s for mode\n", buf); + goto done_psm_store; + } + + if ((val != PMIC_PWM_MODE) && (val != PMIC_AUTO_MODE)) { + pr_err(" Invalid number %d for mode\n", val); + goto done_psm_store; + } + + if (val != reg->mode) { + ret = rpm_regulator_set_mode(reg->reg, val); + if (ret) { + pr_err( \ + "Fail to set PMIC SW Mode:%d for %s\n", + val, reg->name); + goto done_psm_store; + } + reg->mode = val; + } + +done_psm_store: + mutex_unlock(&psm_mutex); + return count; +} + +static int check_sensor_id(int sensor_id) +{ + int i = 0; + bool hw_id_found = false; + int ret = 0; + + for (i = 0; i < max_tsens_num; i++) { + if (sensor_id == tsens_id_map[i]) { + hw_id_found = true; + break; + } + } + if (!hw_id_found) { + pr_err("%s: Invalid sensor hw id :%d\n", __func__, sensor_id); + return -EINVAL; + } + + return ret; +} + +static int create_sensor_id_map(void) +{ + int i = 0; + int ret = 0; + + tsens_id_map = kzalloc(sizeof(int) * max_tsens_num, + GFP_KERNEL); + if (!tsens_id_map) { + pr_err("%s: Cannot allocate memory for tsens_id_map\n", + __func__); + return -ENOMEM; + } + + for (i = 0; i < max_tsens_num; i++) { + ret = tsens_get_hw_id_mapping(i, &tsens_id_map[i]); + /* If return -ENXIO, hw_id is default in sequence */ + if (ret) { + if (ret == -ENXIO) { + tsens_id_map[i] = i; + ret = 0; + } else { + pr_err( \ + "%s: Failed to get hw id for sw id %d\n", + __func__, i); + goto fail; + } + } + } + + return ret; +fail: + kfree(tsens_id_map); + return ret; +} + +/* 1:enable, 0:disable */ +static int vdd_restriction_apply_all(int en) +{ + int i = 0; + int en_cnt = 0; + int dis_cnt = 0; + int fail_cnt = 0; + int ret = 0; + + for (i = 0; i < rails_cnt; i++) { + if (rails[i].freq_req == 1 && freq_table_get) + ret = vdd_restriction_apply_freq(&rails[i], + en ? 0 : -1); + else + ret = vdd_restriction_apply_voltage(&rails[i], + en ? 0 : -1); + if (ret) { + pr_err("Cannot set voltage for %s", rails[i].name); + fail_cnt++; + } else { + if (en) + en_cnt++; + else + dis_cnt++; + } + } + + /* As long as one rail is enabled, vdd rstr is enabled */ + if (en && en_cnt) + vdd_rstr_en.enabled = 1; + else if (!en && (dis_cnt == rails_cnt)) + vdd_rstr_en.enabled = 0; + + /* + * Check fail_cnt again to make sure all of the rails are applied + * restriction successfully or not + */ + if (fail_cnt) + return -EFAULT; + return ret; +} + +static int msm_thermal_get_freq_table(void) +{ + int ret = 0; + int i = 0; + + table = cpufreq_frequency_get_table(0); + if (table == NULL) { + pr_debug("%s: error reading cpufreq table\n", KBUILD_MODNAME); + ret = -EINVAL; + goto fail; + } + + while (table[i].frequency != CPUFREQ_TABLE_END) + i++; + + limit_idx_low = 0; + limit_idx_high = limit_idx = i - 1; + BUG_ON(limit_idx_high <= 0 || limit_idx_high <= limit_idx_low); +fail: + return ret; +} + +static int set_and_activate_threshold(uint32_t sensor_id, + struct sensor_threshold *threshold) +{ + int ret = 0; + + ret = sensor_set_trip(sensor_id, threshold); + if (ret != 0) { + pr_err("%s: Error in setting trip %d\n", + KBUILD_MODNAME, threshold->trip); + goto set_done; + } + + ret = sensor_activate_trip(sensor_id, threshold, true); + if (ret != 0) { + pr_err("%s: Error in enabling trip %d\n", + KBUILD_MODNAME, threshold->trip); + goto set_done; + } + +set_done: + return ret; +} + +static int therm_get_temp(uint32_t id, enum sensor_id_type type, long *temp) +{ + int ret = 0; + struct tsens_device tsens_dev; + + if (!temp) { + pr_err("Invalid value\n"); + ret = -EINVAL; + goto get_temp_exit; + } + + switch (type) { + case THERM_ZONE_ID: + tsens_dev.sensor_num = tsens_id_map[id]; + break; + case THERM_TSENS_ID: + tsens_dev.sensor_num = id; + break; + default: + pr_err("Invalid type\n"); + ret = -EINVAL; + goto get_temp_exit; + break; + } + + ret = tsens_get_temp(&tsens_dev, temp); + if (ret) { + pr_err("Unable to read TSENS sensor %d\n", + tsens_dev.sensor_num); + goto get_temp_exit; + } + +get_temp_exit: + return ret; +} + +static int set_threshold(uint32_t zone_id, + struct sensor_threshold *threshold) +{ + int i = 0, ret = 0; + long temp; + + if ((!threshold) || (zone_id >= max_tsens_num)) { + pr_err("%s: Invalid input\n", KBUILD_MODNAME); + ret = -EINVAL; + goto set_threshold_exit; + } + + ret = therm_get_temp(zone_id, THERM_ZONE_ID, &temp); + if (ret) + goto set_threshold_exit; + + while (i < MAX_THRESHOLD) { + switch (threshold[i].trip) { + case THERMAL_TRIP_CONFIGURABLE_HI: + if (threshold[i].temp >= temp) { + ret = set_and_activate_threshold(zone_id, + &threshold[i]); + if (ret) + goto set_threshold_exit; + } + break; + case THERMAL_TRIP_CONFIGURABLE_LOW: + if (threshold[i].temp <= temp) { + ret = set_and_activate_threshold(zone_id, + &threshold[i]); + if (ret) + goto set_threshold_exit; + } + break; + default: + break; + } + i++; + } +set_threshold_exit: + return ret; +} + +#ifdef CONFIG_SMP +static void __ref do_core_control(long temp) +{ + int i = 0; + int ret = 0; + + if (!core_control_enabled) + return; + + mutex_lock(&core_control_mutex); + if (msm_thermal_info.core_control_mask && + temp >= msm_thermal_info.core_limit_temp_degC) { + for (i = num_possible_cpus(); i > 0; i--) { + if (!(msm_thermal_info.core_control_mask & BIT(i))) + continue; + if (cpus_offlined & BIT(i) && !cpu_online(i)) + continue; + pr_info("%s: Set Offline: CPU%d Temp: %ld\n", + KBUILD_MODNAME, i, temp); + ret = cpu_down(i); + if (ret) + pr_err("%s: Error %d offline core %d\n", + KBUILD_MODNAME, ret, i); + cpus_offlined |= BIT(i); + break; + } + } else if (msm_thermal_info.core_control_mask && cpus_offlined && + temp <= (msm_thermal_info.core_limit_temp_degC - + msm_thermal_info.core_temp_hysteresis_degC)) { + for (i = 0; i < num_possible_cpus(); i++) { + if (!(cpus_offlined & BIT(i))) + continue; + cpus_offlined &= ~BIT(i); + pr_info("%s: Allow Online CPU%d Temp: %ld\n", + KBUILD_MODNAME, i, temp); + /* + * If this core is already online, then bring up the + * next offlined core. + */ + if (cpu_online(i)) + continue; + ret = cpu_up(i); + if (ret) + pr_err("%s: Error %d online core %d\n", + KBUILD_MODNAME, ret, i); + break; + } + } + mutex_unlock(&core_control_mutex); +} +/* Call with core_control_mutex locked */ +static int __ref update_offline_cores(int val) +{ + uint32_t cpu = 0; + int ret = 0; + + if (!core_control_enabled) + return 0; + + cpus_offlined = msm_thermal_info.core_control_mask & val; + + for_each_possible_cpu(cpu) { + if (!(cpus_offlined & BIT(cpu))) + continue; + if (!cpu_online(cpu)) + continue; + ret = cpu_down(cpu); + if (ret) + pr_err("%s: Unable to offline cpu%d\n", + KBUILD_MODNAME, cpu); + } + return ret; +} + +static __ref int do_hotplug(void *data) +{ + int ret = 0; + uint32_t cpu = 0, mask = 0; + + if (!core_control_enabled) + return -EINVAL; + + while (!kthread_should_stop()) { + wait_for_completion(&hotplug_notify_complete); + INIT_COMPLETION(hotplug_notify_complete); + mask = 0; + + mutex_lock(&core_control_mutex); + for_each_possible_cpu(cpu) { + if (hotplug_enabled && + cpus[cpu].hotplug_thresh_clear) { + set_threshold(cpus[cpu].sensor_id, + &cpus[cpu].threshold[HOTPLUG_THRESHOLD_HIGH]); + + cpus[cpu].hotplug_thresh_clear = false; + } + if (cpus[cpu].offline || cpus[cpu].user_offline) + mask |= BIT(cpu); + } + if (mask != cpus_offlined) + update_offline_cores(mask); + mutex_unlock(&core_control_mutex); + sysfs_notify(cc_kobj, NULL, "cpus_offlined"); + } + + return ret; +} +#else +static void do_core_control(long temp) +{ + return; +} + +static __ref int do_hotplug(void *data) +{ + return 0; +} +#endif + +static int do_ocr(void) +{ + long temp = 0; + int ret = 0; + int i = 0, j = 0; + int auto_cnt = 0; + + if (!ocr_enabled) + return ret; + + mutex_lock(&ocr_mutex); + for (i = 0; i < max_tsens_num; i++) { + ret = therm_get_temp(tsens_id_map[i], THERM_TSENS_ID, &temp); + if (ret) { + pr_debug("%s: Unable to read TSENS sensor %d\n", + __func__, tsens_id_map[i]); + auto_cnt++; + continue; + } + + if (temp > msm_thermal_info.ocr_temp_degC) { + if (ocr_rails[0].init != OPTIMUM_CURRENT_NR) + for (j = 0; j < ocr_rail_cnt; j++) + ocr_rails[j].init = OPTIMUM_CURRENT_NR; + ret = ocr_set_mode_all(OPTIMUM_CURRENT_MAX); + if (ret) + pr_err("Error setting max optimum current\n"); + goto do_ocr_exit; + } else if (temp <= (msm_thermal_info.ocr_temp_degC - + msm_thermal_info.ocr_temp_hyst_degC)) + auto_cnt++; + } + + if (auto_cnt == max_tsens_num || + ocr_rails[0].init != OPTIMUM_CURRENT_NR) { + /* 'init' not equal to OPTIMUM_CURRENT_NR means this is the + ** first polling iteration after device probe. During first + ** iteration, if temperature is less than the set point, clear + ** the max current request made and reset the 'init'. + */ + if (ocr_rails[0].init != OPTIMUM_CURRENT_NR) + for (j = 0; j < ocr_rail_cnt; j++) + ocr_rails[j].init = OPTIMUM_CURRENT_NR; + ret = ocr_set_mode_all(OPTIMUM_CURRENT_MIN); + if (ret) { + pr_err("Error setting min optimum current\n"); + goto do_ocr_exit; + } + } + +do_ocr_exit: + mutex_unlock(&ocr_mutex); + return ret; +} + +static int do_vdd_restriction(void) +{ + long temp = 0; + int ret = 0; + int i = 0; + int dis_cnt = 0; + + if (!vdd_rstr_enabled) + return ret; + + if (usefreq && !freq_table_get) { + if (check_freq_table()) + return ret; + } + + mutex_lock(&vdd_rstr_mutex); + for (i = 0; i < max_tsens_num; i++) { + ret = therm_get_temp(tsens_id_map[i], THERM_TSENS_ID, &temp); + if (ret) { + pr_debug("Unable to read TSENS sensor %d\n", + tsens_id_map[i]); + dis_cnt++; + continue; + } + if (temp <= msm_thermal_info.vdd_rstr_temp_degC) { + ret = vdd_restriction_apply_all(1); + if (ret) { + pr_err( \ + "Enable vdd rstr votlage for all failed\n"); + goto exit; + } + goto exit; + } else if (temp > msm_thermal_info.vdd_rstr_temp_hyst_degC) + dis_cnt++; + } + if (dis_cnt == max_tsens_num) { + ret = vdd_restriction_apply_all(0); + if (ret) { + pr_err("Disable vdd rstr votlage for all failed\n"); + goto exit; + } + } +exit: + mutex_unlock(&vdd_rstr_mutex); + return ret; +} + +static int do_psm(void) +{ + long temp = 0; + int ret = 0; + int i = 0; + int auto_cnt = 0; + + mutex_lock(&psm_mutex); + for (i = 0; i < max_tsens_num; i++) { + ret = therm_get_temp(tsens_id_map[i], THERM_TSENS_ID, &temp); + if (ret) { + pr_debug("%s: Unable to read TSENS sensor %d\n", + __func__, tsens_id_map[i]); + auto_cnt++; + continue; + } + + /* + * As long as one sensor is above the threshold, set PWM mode + * on all rails, and loop stops. Set auto mode when all rails + * are below thershold + */ + if (temp > msm_thermal_info.psm_temp_degC) { + ret = psm_set_mode_all(PMIC_PWM_MODE); + if (ret) { + pr_err("Set pwm mode for all failed\n"); + goto exit; + } + break; + } else if (temp <= msm_thermal_info.psm_temp_hyst_degC) + auto_cnt++; + } + + if (auto_cnt == max_tsens_num) { + ret = psm_set_mode_all(PMIC_AUTO_MODE); + if (ret) { + pr_err("Set auto mode for all failed\n"); + goto exit; + } + } + +exit: + mutex_unlock(&psm_mutex); + return ret; +} + +static void __ref do_freq_control(long temp) +{ + uint32_t cpu = 0; + uint32_t max_freq = cpus[cpu].limited_max_freq; + + if (temp >= msm_thermal_info.limit_temp_degC) { + if (limit_idx == limit_idx_low) + return; + + limit_idx -= msm_thermal_info.bootup_freq_step; + if (limit_idx < limit_idx_low) + limit_idx = limit_idx_low; + max_freq = table[limit_idx].frequency; + } else if (temp < msm_thermal_info.limit_temp_degC - + msm_thermal_info.temp_hysteresis_degC) { + if (limit_idx == limit_idx_high) + return; + + limit_idx += msm_thermal_info.bootup_freq_step; + if (limit_idx >= limit_idx_high) { + limit_idx = limit_idx_high; + max_freq = UINT_MAX; + } else + max_freq = table[limit_idx].frequency; + } + + if (max_freq == cpus[cpu].limited_max_freq) + return; + + /* Update new limits */ + get_online_cpus(); + for_each_possible_cpu(cpu) { + if (!(msm_thermal_info.bootup_freq_control_mask & BIT(cpu))) + continue; + cpus[cpu].limited_max_freq = max_freq; + update_cpu_freq(cpu); + } + put_online_cpus(); +} + +static void __ref check_temp(struct work_struct *work) +{ + static int limit_init; + long temp = 0; + int ret = 0; + + ret = therm_get_temp(msm_thermal_info.sensor_id, THERM_TSENS_ID, &temp); + if (ret) { + pr_debug("Unable to read TSENS sensor %d\n", + msm_thermal_info.sensor_id); + goto reschedule; + } + + if (!limit_init) { + ret = msm_thermal_get_freq_table(); + if (ret) + goto reschedule; + else + limit_init = 1; + } + + do_core_control(temp); + do_vdd_restriction(); + do_psm(); + do_ocr(); + do_freq_control(temp); + //pr_info("%s: worker is alive!\n", KBUILD_MODNAME); +reschedule: + if (enabled) + schedule_delayed_work(&check_temp_work, + msecs_to_jiffies(msm_thermal_info.poll_ms)); +} + +static int __ref msm_thermal_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + uint32_t cpu = (uint32_t)hcpu; + + if (action == CPU_UP_PREPARE || action == CPU_UP_PREPARE_FROZEN) { + if (core_control_enabled && + (msm_thermal_info.core_control_mask & BIT(cpu)) && + (cpus_offlined & BIT(cpu))) { + pr_debug( + "%s: Preventing cpu%d from coming online.\n", + KBUILD_MODNAME, cpu); + return NOTIFY_BAD; + } + } + + + return NOTIFY_OK; +} + +static struct notifier_block __refdata msm_thermal_cpu_notifier = { + .notifier_call = msm_thermal_cpu_callback, +}; + +static void thermal_rtc_setup(void) +{ + ktime_t wakeup_time; + ktime_t curr_time; + + curr_time = alarm_get_elapsed_realtime(); + wakeup_time = ktime_add_us(curr_time, + (wakeup_ms * USEC_PER_MSEC)); + alarm_start_range(&thermal_rtc, wakeup_time, + wakeup_time); + pr_debug("%s: Current Time: %ld %ld, Alarm set to: %ld %ld\n", + KBUILD_MODNAME, + ktime_to_timeval(curr_time).tv_sec, + ktime_to_timeval(curr_time).tv_usec, + ktime_to_timeval(wakeup_time).tv_sec, + ktime_to_timeval(wakeup_time).tv_usec); + +} + +static void timer_work_fn(struct work_struct *work) +{ + sysfs_notify(tt_kobj, NULL, "wakeup_ms"); +} + +static void thermal_rtc_callback(struct alarm *al) +{ + struct timeval ts; + ts = ktime_to_timeval(alarm_get_elapsed_realtime()); + schedule_work(&timer_work); + pr_debug("%s: Time on alarm expiry: %ld %ld\n", KBUILD_MODNAME, + ts.tv_sec, ts.tv_usec); +} + +static int hotplug_notify(enum thermal_trip_type type, int temp, void *data) +{ + struct cpu_info *cpu_node = (struct cpu_info *)data; + + pr_info("%s: %s reach temp threshold: %d\n", KBUILD_MODNAME, + cpu_node->sensor_type, temp); + + if (!(msm_thermal_info.core_control_mask & BIT(cpu_node->cpu))) + return 0; + switch (type) { + case THERMAL_TRIP_CONFIGURABLE_HI: + if (!(cpu_node->offline)) + cpu_node->offline = 1; + break; + case THERMAL_TRIP_CONFIGURABLE_LOW: + if (cpu_node->offline) + cpu_node->offline = 0; + break; + default: + break; + } + if (hotplug_task) { + cpu_node->hotplug_thresh_clear = true; + complete(&hotplug_notify_complete); + } else { + pr_err("%s: Hotplug task is not initialized\n", KBUILD_MODNAME); + } + return 0; +} +/* Adjust cpus offlined bit based on temperature reading. */ +static int hotplug_init_cpu_offlined(void) +{ + long temp = 0; + uint32_t cpu = 0; + + if (!hotplug_enabled) + return 0; + + mutex_lock(&core_control_mutex); + for_each_possible_cpu(cpu) { + if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu))) + continue; + if (therm_get_temp(cpus[cpu].sensor_id, cpus[cpu].id_type, + &temp)) { + pr_err("%s: Unable to read TSENS sensor %d\n", + KBUILD_MODNAME, cpus[cpu].sensor_id); + mutex_unlock(&core_control_mutex); + return -EINVAL; + } + + if (temp >= msm_thermal_info.hotplug_temp_degC) + cpus[cpu].offline = 1; + else if (temp <= (msm_thermal_info.hotplug_temp_degC - + msm_thermal_info.hotplug_temp_hysteresis_degC)) + cpus[cpu].offline = 0; + } + mutex_unlock(&core_control_mutex); + + if (hotplug_task) + complete(&hotplug_notify_complete); + else { + pr_err("%s: Hotplug task is not initialized\n", + KBUILD_MODNAME); + return -EINVAL; + } + return 0; +} + +static void hotplug_init(void) +{ + uint32_t cpu = 0; + struct sensor_threshold *hi_thresh = NULL, *low_thresh = NULL; + + if (hotplug_task) + return; + + if (!hotplug_enabled) + goto init_kthread; + + for_each_possible_cpu(cpu) { + cpus[cpu].sensor_id = + sensor_get_id((char *)cpus[cpu].sensor_type); + cpus[cpu].id_type = THERM_ZONE_ID; + if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu))) + continue; + + hi_thresh = &cpus[cpu].threshold[HOTPLUG_THRESHOLD_HIGH]; + low_thresh = &cpus[cpu].threshold[HOTPLUG_THRESHOLD_LOW]; + hi_thresh->temp = msm_thermal_info.hotplug_temp_degC; + hi_thresh->trip = THERMAL_TRIP_CONFIGURABLE_HI; + low_thresh->temp = msm_thermal_info.hotplug_temp_degC - + msm_thermal_info.hotplug_temp_hysteresis_degC; + low_thresh->trip = THERMAL_TRIP_CONFIGURABLE_LOW; + hi_thresh->notify = low_thresh->notify = hotplug_notify; + hi_thresh->data = low_thresh->data = (void *)&cpus[cpu]; + + set_threshold(cpus[cpu].sensor_id, hi_thresh); + } +init_kthread: + init_completion(&hotplug_notify_complete); + hotplug_task = kthread_run(do_hotplug, NULL, "msm_thermal:hotplug"); + if (IS_ERR(hotplug_task)) { + pr_err("%s: Failed to create do_hotplug thread\n", + KBUILD_MODNAME); + return; + } + /* + * Adjust cpus offlined bit when hotplug intitializes so that the new + * cpus offlined state is based on hotplug threshold range + */ + if (hotplug_init_cpu_offlined()) + kthread_stop(hotplug_task); +} + +static __ref int do_freq_mitigation(void *data) +{ + int ret = 0; + uint32_t cpu = 0, max_freq_req = 0, min_freq_req = 0; + + while (!kthread_should_stop()) { + wait_for_completion(&freq_mitigation_complete); + INIT_COMPLETION(freq_mitigation_complete); + + get_online_cpus(); + for_each_possible_cpu(cpu) { + max_freq_req = (cpus[cpu].max_freq) ? + msm_thermal_info.freq_limit : + UINT_MAX; + max_freq_req = min(max_freq_req, + cpus[cpu].user_max_freq); + + min_freq_req = max(min_freq_limit, + cpus[cpu].user_min_freq); + + if ((max_freq_req == cpus[cpu].limited_max_freq) + && (min_freq_req == + cpus[cpu].limited_min_freq)) + goto reset_threshold; + + cpus[cpu].limited_max_freq = max_freq_req; + cpus[cpu].limited_min_freq = min_freq_req; + update_cpu_freq(cpu); +reset_threshold: + if (freq_mitigation_enabled && + cpus[cpu].freq_thresh_clear) { + set_threshold(cpus[cpu].sensor_id, + &cpus[cpu].threshold[FREQ_THRESHOLD_HIGH]); + + cpus[cpu].freq_thresh_clear = false; + } + } + put_online_cpus(); + } + return ret; +} + +static int freq_mitigation_notify(enum thermal_trip_type type, + int temp, void *data) +{ + struct cpu_info *cpu_node = (struct cpu_info *) data; + + pr_debug("%s: %s reached temp threshold: %d\n", KBUILD_MODNAME, + cpu_node->sensor_type, temp); + + if (!(msm_thermal_info.freq_mitig_control_mask & + BIT(cpu_node->cpu))) + return 0; + + switch (type) { + case THERMAL_TRIP_CONFIGURABLE_HI: + if (!cpu_node->max_freq) { + pr_info("%s: Mitigating cpu %d frequency to %d\n", + KBUILD_MODNAME, cpu_node->cpu, + msm_thermal_info.freq_limit); + + cpu_node->max_freq = true; + } + break; + case THERMAL_TRIP_CONFIGURABLE_LOW: + if (cpu_node->max_freq) { + pr_info("%s: Removing frequency mitigation for cpu%d\n", + KBUILD_MODNAME, cpu_node->cpu); + + cpu_node->max_freq = false; + } + break; + default: + break; + } + + if (freq_mitigation_task) { + cpu_node->freq_thresh_clear = true; + complete(&freq_mitigation_complete); + } else { + pr_err("%s: Frequency mitigation task is not initialized\n", + KBUILD_MODNAME); + } + + return 0; +} + +static void freq_mitigation_init(void) +{ + uint32_t cpu = 0; + struct sensor_threshold *hi_thresh = NULL, *low_thresh = NULL; + + if (freq_mitigation_task) + return; + if (!freq_mitigation_enabled) + goto init_freq_thread; + + for_each_possible_cpu(cpu) { + if (!(msm_thermal_info.freq_mitig_control_mask & BIT(cpu))) + continue; + hi_thresh = &cpus[cpu].threshold[FREQ_THRESHOLD_HIGH]; + low_thresh = &cpus[cpu].threshold[FREQ_THRESHOLD_LOW]; + + hi_thresh->temp = msm_thermal_info.freq_mitig_temp_degc; + hi_thresh->trip = THERMAL_TRIP_CONFIGURABLE_HI; + low_thresh->temp = msm_thermal_info.freq_mitig_temp_degc - + msm_thermal_info.freq_mitig_temp_hysteresis_degc; + low_thresh->trip = THERMAL_TRIP_CONFIGURABLE_LOW; + hi_thresh->notify = low_thresh->notify = + freq_mitigation_notify; + hi_thresh->data = low_thresh->data = (void *)&cpus[cpu]; + + set_threshold(cpus[cpu].sensor_id, hi_thresh); + } +init_freq_thread: + init_completion(&freq_mitigation_complete); + freq_mitigation_task = kthread_run(do_freq_mitigation, NULL, + "msm_thermal:freq_mitig"); + + if (IS_ERR(freq_mitigation_task)) { + pr_err("%s: Failed to create frequency mitigation thread\n", + KBUILD_MODNAME); + return; + } +} + +int msm_thermal_set_frequency(uint32_t cpu, uint32_t freq, bool is_max) +{ + int ret = 0; + + if (cpu >= num_possible_cpus()) { + pr_err("%s: Invalid input\n", KBUILD_MODNAME); + ret = -EINVAL; + goto set_freq_exit; + } + + if (is_max) { + if (cpus[cpu].user_max_freq == freq) + goto set_freq_exit; + + cpus[cpu].user_max_freq = freq; + } else { + if (cpus[cpu].user_min_freq == freq) + goto set_freq_exit; + + cpus[cpu].user_min_freq = freq; + } + + if (freq_mitigation_task) { + complete(&freq_mitigation_complete); + } else { + pr_err("%s: Frequency mitigation task is not initialized\n", + KBUILD_MODNAME); + ret = -ESRCH; + goto set_freq_exit; + } + +set_freq_exit: + return ret; +} + +/* + * We will reset the cpu frequencies limits here. The core online/offline + * status will be carried over to the process stopping the msm_thermal, as + * we dont want to online a core and bring in the thermal issues. + */ +static void __ref disable_msm_thermal(void) +{ + uint32_t cpu = 0; + + /* make sure check_temp is no longer running */ + cancel_delayed_work_sync(&check_temp_work); + + get_online_cpus(); + for_each_possible_cpu(cpu) { + if (cpus[cpu].limited_max_freq == UINT_MAX && + cpus[cpu].limited_min_freq == 0) + continue; + cpus[cpu].limited_max_freq = UINT_MAX; + cpus[cpu].limited_min_freq = 0; + update_cpu_freq(cpu); + } + put_online_cpus(); +} + +static int __ref set_enabled(const char *val, const struct kernel_param *kp) +{ + int ret = 0; + + if (*val == '0' || *val == 'n' || *val == 'N') { + enabled = 0; + disable_msm_thermal(); + hotplug_init(); + freq_mitigation_init(); + pr_info("%s: intellithermal disabled!\n", KBUILD_MODNAME); + } else { + if (!enabled) { + enabled = 1; + schedule_delayed_work(&check_temp_work, + msecs_to_jiffies(msm_thermal_info.poll_ms)); + pr_info("%s: rescheduling...\n", KBUILD_MODNAME); + } else + pr_info("%s: already running...\n", KBUILD_MODNAME); + } + + pr_info("%s: enabled = %d\n", KBUILD_MODNAME, enabled); + + return ret; +} + +static struct kernel_param_ops module_ops = { + .set = set_enabled, + .get = param_get_bool, +}; + +module_param_cb(enabled, &module_ops, &enabled, 0644); +MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu"); + +static ssize_t show_cc_enabled(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", core_control_enabled); +} + +static ssize_t __ref store_cc_enabled(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + int val = 0; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_err("%s: Invalid input %s\n", KBUILD_MODNAME, buf); + goto done_store_cc; + } + + if (core_control_enabled == !!val) + goto done_store_cc; + + core_control_enabled = !!val; + if (core_control_enabled) { + pr_info("%s: Core control enabled\n", KBUILD_MODNAME); + register_cpu_notifier(&msm_thermal_cpu_notifier); + if (hotplug_task) + complete(&hotplug_notify_complete); + else + pr_err("%s: Hotplug task is not initialized\n", + KBUILD_MODNAME); + } else { + pr_info("%s: Core control disabled\n", KBUILD_MODNAME); + unregister_cpu_notifier(&msm_thermal_cpu_notifier); + } + +done_store_cc: + return count; +} + +static ssize_t show_cpus_offlined(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", cpus_offlined); +} + +static ssize_t __ref store_cpus_offlined(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + uint32_t val = 0; + uint32_t cpu; + + mutex_lock(&core_control_mutex); + ret = kstrtouint(buf, 10, &val); + if (ret) { + pr_err("%s: Invalid input %s\n", KBUILD_MODNAME, buf); + goto done_cc; + } + + if (enabled) { + pr_err("%s: Ignoring request; polling thread is enabled.\n", + KBUILD_MODNAME); + goto done_cc; + } + + for_each_possible_cpu(cpu) { + if (!(msm_thermal_info.core_control_mask & BIT(cpu))) + continue; + cpus[cpu].user_offline = !!(val & BIT(cpu)); + } + + if (hotplug_task) + complete(&hotplug_notify_complete); + else + pr_err("%s: Hotplug task is not initialized\n", KBUILD_MODNAME); +done_cc: + mutex_unlock(&core_control_mutex); + return count; +} + +static __refdata struct kobj_attribute cc_enabled_attr = +__ATTR(enabled, 0644, show_cc_enabled, store_cc_enabled); + +static __refdata struct kobj_attribute cpus_offlined_attr = +__ATTR(cpus_offlined, 0644, show_cpus_offlined, store_cpus_offlined); + +static __refdata struct attribute *cc_attrs[] = { + &cc_enabled_attr.attr, + &cpus_offlined_attr.attr, + NULL, +}; + +static __refdata struct attribute_group cc_attr_group = { + .attrs = cc_attrs, +}; + +static ssize_t show_wakeup_ms(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", wakeup_ms); +} + +static ssize_t store_wakeup_ms(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret; + ret = kstrtouint(buf, 10, &wakeup_ms); + + if (ret) { + pr_err("%s: Trying to set invalid wakeup timer\n", + KBUILD_MODNAME); + return ret; + } + + if (wakeup_ms > 0) { + thermal_rtc_setup(); + pr_debug("%s: Timer started for %ums\n", KBUILD_MODNAME, + wakeup_ms); + } else { + ret = alarm_cancel(&thermal_rtc); + if (ret) + pr_debug("%s: Timer canceled\n", KBUILD_MODNAME); + else + pr_debug("%s: No active timer present to cancel\n", + KBUILD_MODNAME); + + } + return count; +} + +static __refdata struct kobj_attribute timer_attr = +__ATTR(wakeup_ms, 0644, show_wakeup_ms, store_wakeup_ms); + +static __refdata struct attribute *tt_attrs[] = { + &timer_attr.attr, + NULL, +}; + +static __refdata struct attribute_group tt_attr_group = { + .attrs = tt_attrs, +}; + +static __init int msm_thermal_add_cc_nodes(void) +{ + struct kobject *module_kobj = NULL; + int ret = 0; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module\n", + KBUILD_MODNAME); + ret = -ENOENT; + goto done_cc_nodes; + } + + cc_kobj = kobject_create_and_add("core_control", module_kobj); + if (!cc_kobj) { + pr_err("%s: cannot create core control kobj\n", + KBUILD_MODNAME); + ret = -ENOMEM; + goto done_cc_nodes; + } + + ret = sysfs_create_group(cc_kobj, &cc_attr_group); + if (ret) { + pr_err("%s: cannot create group\n", KBUILD_MODNAME); + goto done_cc_nodes; + } + + return 0; + +done_cc_nodes: + if (cc_kobj) + kobject_del(cc_kobj); + return ret; +} + +static __init int msm_thermal_add_timer_nodes(void) +{ + struct kobject *module_kobj = NULL; + int ret = 0; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module\n", + KBUILD_MODNAME); + ret = -ENOENT; + goto failed; + } + + tt_kobj = kobject_create_and_add("thermal_timer", module_kobj); + if (!tt_kobj) { + pr_err("%s: cannot create timer kobj\n", + KBUILD_MODNAME); + ret = -ENOMEM; + goto failed; + } + + ret = sysfs_create_group(tt_kobj, &tt_attr_group); + if (ret) { + pr_err("%s: cannot create group\n", KBUILD_MODNAME); + goto failed; + } + + return 0; + +failed: + if (tt_kobj) + kobject_del(tt_kobj); + return ret; +} + +static ssize_t show_thermal_stats(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + + int i = 0; + int tmp = 0; + + /* clear out old stats */ + msm_thermal_stats.throttled = 0; + msm_thermal_stats.warning = 0; + msm_thermal_stats.normal = 0; + + for (i = 0; i < MAX_HISTORY_SZ; i++) { + tmp = msm_thermal_stats.temp_history[i]; + if (tmp >= msm_thermal_info.limit_temp_degC) + msm_thermal_stats.throttled++; + else if (tmp < msm_thermal_info.limit_temp_degC && + tmp >= (msm_thermal_info.limit_temp_degC - + msm_thermal_info.temp_hysteresis_degC)) + msm_thermal_stats.warning++; + else + msm_thermal_stats.normal++; + } + return snprintf(buf, PAGE_SIZE, "%u %u %u\n", + msm_thermal_stats.throttled, + msm_thermal_stats.warning, + msm_thermal_stats.normal); +} + +static __refdata struct kobj_attribute msm_thermal_stat_attr = +__ATTR(statistics, 0444, show_thermal_stats, NULL); + +static __refdata struct attribute *msm_thermal_stat_attrs[] = { + &msm_thermal_stat_attr.attr, + NULL, +}; + +static __refdata struct attribute_group msm_thermal_stat_attr_group = { + .attrs = msm_thermal_stat_attrs, +}; + +static __init int msm_thermal_add_stat_nodes(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *stat_kobj = NULL; + int ret = 0; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module\n", + KBUILD_MODNAME); + ret = -ENOENT; + goto done_stat_nodes; + } + + stat_kobj = kobject_create_and_add("thermal_stats", module_kobj); + if (!stat_kobj) { + pr_err("%s: cannot create core control kobj\n", + KBUILD_MODNAME); + ret = -ENOMEM; + goto done_stat_nodes; + } + + ret = sysfs_create_group(stat_kobj, &msm_thermal_stat_attr_group); + if (ret) { + pr_err("%s: cannot create group\n", KBUILD_MODNAME); + goto done_stat_nodes; + } + + return 0; + +done_stat_nodes: + if (stat_kobj) + kobject_del(stat_kobj); + return ret; +} + +int __devinit msm_thermal_init(struct msm_thermal_data *pdata) +{ + int ret = 0; + uint32_t cpu; + + for_each_possible_cpu(cpu) { + cpus[cpu].cpu = cpu; + cpus[cpu].offline = 0; + cpus[cpu].user_offline = 0; + cpus[cpu].hotplug_thresh_clear = false; + cpus[cpu].max_freq = false; + cpus[cpu].user_max_freq = UINT_MAX; + cpus[cpu].user_min_freq = 0; + cpus[cpu].limited_max_freq = UINT_MAX; + cpus[cpu].limited_min_freq = 0; + cpus[cpu].freq_thresh_clear = false; + } + BUG_ON(!pdata); + tsens_get_max_sensor_num(&max_tsens_num); + memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data)); + + if (create_sensor_id_map()) + return -EINVAL; + if (check_sensor_id(msm_thermal_info.sensor_id)) + return -EINVAL; + + enabled = 1; + pr_info("%s: polling enabled!\n", KBUILD_MODNAME); + ret = cpufreq_register_notifier(&msm_thermal_cpufreq_notifier, + CPUFREQ_POLICY_NOTIFIER); + if (ret) + pr_err("%s: cannot register cpufreq notifier\n", + KBUILD_MODNAME); + INIT_DELAYED_WORK(&check_temp_work, check_temp); + schedule_delayed_work(&check_temp_work, 0); + + if (num_possible_cpus() > 1) + register_cpu_notifier(&msm_thermal_cpu_notifier); + + return ret; +} + +static int ocr_reg_init(struct platform_device *pdev) +{ + int ret = 0; + int i, j; + + for (i = 0; i < ocr_rail_cnt; i++) { + /* Check if vdd_restriction has already initialized any + * regualtor handle. If so use the same handle.*/ + for (j = 0; j < rails_cnt; j++) { + if (!strcmp(ocr_rails[i].name, rails[j].name)) { + if (rails[j].reg == NULL) + break; + ocr_rails[i].phase_reg = rails[j].reg; + goto reg_init; + } + + } + ocr_rails[i].phase_reg = devm_regulator_get(&pdev->dev, + ocr_rails[i].name); + if (IS_ERR_OR_NULL(ocr_rails[i].phase_reg)) { + ret = PTR_ERR(ocr_rails[i].phase_reg); + if (ret != -EPROBE_DEFER) { + pr_err("%s, could not get regulator: %s\n", + __func__, ocr_rails[i].name); + ocr_rails[i].phase_reg = NULL; + ocr_rails[i].mode = 0; + ocr_rails[i].init = 0; + } + return ret; + } +reg_init: + ocr_rails[i].mode = OPTIMUM_CURRENT_MIN; + } + return ret; +} + +static int vdd_restriction_reg_init(struct platform_device *pdev) +{ + int ret = 0; + int i; + + for (i = 0; i < rails_cnt; i++) { + if (rails[i].freq_req == 1) { + usefreq |= BIT(i); + check_freq_table(); + /* + * Restrict frequency by default until we have made + * our first temp reading + */ + if (freq_table_get) + ret = vdd_restriction_apply_freq(&rails[i], 0); + else + pr_info("%s:Defer vdd rstr freq init\n", + __func__); + } else { + rails[i].reg = devm_regulator_get(&pdev->dev, + rails[i].name); + if (IS_ERR_OR_NULL(rails[i].reg)) { + ret = PTR_ERR(rails[i].reg); + if (ret != -EPROBE_DEFER) { + pr_err( \ + "%s, could not get regulator: %s\n", + rails[i].name, __func__); + rails[i].reg = NULL; + rails[i].curr_level = -2; + return ret; + } + return ret; + } + /* + * Restrict votlage by default until we have made + * our first temp reading + */ + ret = vdd_restriction_apply_voltage(&rails[i], 0); + } + } + + return ret; +} + +static int psm_reg_init(struct platform_device *pdev) +{ + int ret = 0; + int i = 0; + int j = 0; + + for (i = 0; i < psm_rails_cnt; i++) { + psm_rails[i].reg = rpm_regulator_get(&pdev->dev, + psm_rails[i].name); + if (IS_ERR_OR_NULL(psm_rails[i].reg)) { + ret = PTR_ERR(psm_rails[i].reg); + if (ret != -EPROBE_DEFER) { + pr_err("%s, could not get rpm regulator: %s\n", + psm_rails[i].name, __func__); + psm_rails[i].reg = NULL; + goto psm_reg_exit; + } + return ret; + } + /* Apps default vote for PWM mode */ + psm_rails[i].init = PMIC_PWM_MODE; + ret = rpm_regulator_set_mode(psm_rails[i].reg, + psm_rails[i].init); + if (ret) { + pr_err("%s: Cannot set PMIC PWM mode\n", __func__); + return ret; + } else + psm_rails[i].mode = PMIC_PWM_MODE; + } + + return ret; + +psm_reg_exit: + if (ret) { + for (j = 0; j < i; j++) { + if (psm_rails[j].reg != NULL) + rpm_regulator_put(psm_rails[j].reg); + } + } + + return ret; +} + +static int msm_thermal_add_vdd_rstr_nodes(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *vdd_rstr_kobj = NULL; + struct kobject *vdd_rstr_reg_kobj[MAX_RAILS] = {0}; + int rc = 0; + int i = 0; + + if (!vdd_rstr_probed) { + vdd_rstr_nodes_called = true; + return rc; + } + + if (vdd_rstr_probed && rails_cnt == 0) + return rc; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + rc = -ENOENT; + goto thermal_sysfs_add_exit; + } + + vdd_rstr_kobj = kobject_create_and_add("vdd_restriction", module_kobj); + if (!vdd_rstr_kobj) { + pr_err("%s: cannot create vdd_restriction kobject\n", __func__); + rc = -ENOMEM; + goto thermal_sysfs_add_exit; + } + + rc = sysfs_create_group(vdd_rstr_kobj, &vdd_rstr_en_attribs_gp); + if (rc) { + pr_err("%s: cannot create kobject attribute group\n", __func__); + rc = -ENOMEM; + goto thermal_sysfs_add_exit; + } + + for (i = 0; i < rails_cnt; i++) { + vdd_rstr_reg_kobj[i] = kobject_create_and_add(rails[i].name, + vdd_rstr_kobj); + if (!vdd_rstr_reg_kobj[i]) { + pr_err("%s: cannot create for kobject for %s\n", + __func__, rails[i].name); + rc = -ENOMEM; + goto thermal_sysfs_add_exit; + } + + rails[i].attr_gp.attrs = kzalloc(sizeof(struct attribute *) * 3, + GFP_KERNEL); + if (!rails[i].attr_gp.attrs) { + rc = -ENOMEM; + goto thermal_sysfs_add_exit; + } + + VDD_RES_RW_ATTRIB(rails[i], rails[i].level_attr, 0, level); + VDD_RES_RO_ATTRIB(rails[i], rails[i].value_attr, 1, value); + rails[i].attr_gp.attrs[2] = NULL; + + rc = sysfs_create_group(vdd_rstr_reg_kobj[i], + &rails[i].attr_gp); + if (rc) { + pr_err("%s: cannot create attribute group for %s\n", + __func__, rails[i].name); + goto thermal_sysfs_add_exit; + } + } + + return rc; + +thermal_sysfs_add_exit: + if (rc) { + for (i = 0; i < rails_cnt; i++) { + kobject_del(vdd_rstr_reg_kobj[i]); + kfree(rails[i].attr_gp.attrs); + } + if (vdd_rstr_kobj) + kobject_del(vdd_rstr_kobj); + } + return rc; +} + +static int msm_thermal_add_ocr_nodes(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *ocr_kobj = NULL; + struct kobject *ocr_reg_kobj[MAX_RAILS] = {0}; + int rc = 0; + int i = 0; + + if (!ocr_probed) { + ocr_nodes_called = true; + return rc; + } + + if (ocr_probed && ocr_rail_cnt == 0) + return rc; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + rc = -ENOENT; + goto ocr_node_exit; + } + + ocr_kobj = kobject_create_and_add("opt_curr_req", module_kobj); + if (!ocr_kobj) { + pr_err("%s: cannot create ocr kobject\n", KBUILD_MODNAME); + rc = -ENOMEM; + goto ocr_node_exit; + } + + for (i = 0; i < ocr_rail_cnt; i++) { + ocr_reg_kobj[i] = kobject_create_and_add(ocr_rails[i].name, + ocr_kobj); + if (!ocr_reg_kobj[i]) { + pr_err("%s: cannot create for kobject for %s\n", + KBUILD_MODNAME, ocr_rails[i].name); + rc = -ENOMEM; + goto ocr_node_exit; + } + ocr_rails[i].attr_gp.attrs = kzalloc( \ + sizeof(struct attribute *) * 2, GFP_KERNEL); + if (!ocr_rails[i].attr_gp.attrs) { + rc = -ENOMEM; + goto ocr_node_exit; + } + + OCR_RW_ATTRIB(ocr_rails[i], ocr_rails[i].mode_attr, 0, mode); + ocr_rails[i].attr_gp.attrs[1] = NULL; + + rc = sysfs_create_group(ocr_reg_kobj[i], &ocr_rails[i].attr_gp); + if (rc) { + pr_err("%s: cannot create attribute group for %s\n", + KBUILD_MODNAME, ocr_rails[i].name); + goto ocr_node_exit; + } + } + +ocr_node_exit: + if (rc) { + for (i = 0; i < ocr_rail_cnt; i++) { + if (ocr_reg_kobj[i]) + kobject_del(ocr_reg_kobj[i]); + if (ocr_rails[i].attr_gp.attrs) { + kfree(ocr_rails[i].attr_gp.attrs); + ocr_rails[i].attr_gp.attrs = NULL; + } + } + if (ocr_kobj) + kobject_del(ocr_kobj); + } + return rc; +} + +static int msm_thermal_add_psm_nodes(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *psm_kobj = NULL; + struct kobject *psm_reg_kobj[MAX_RAILS] = {0}; + int rc = 0; + int i = 0; + + if (!psm_probed) { + psm_nodes_called = true; + return rc; + } + + if (psm_probed && psm_rails_cnt == 0) + return rc; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + rc = -ENOENT; + goto psm_node_exit; + } + + psm_kobj = kobject_create_and_add("pmic_sw_mode", module_kobj); + if (!psm_kobj) { + pr_err("%s: cannot create psm kobject\n", KBUILD_MODNAME); + rc = -ENOMEM; + goto psm_node_exit; + } + + for (i = 0; i < psm_rails_cnt; i++) { + psm_reg_kobj[i] = kobject_create_and_add(psm_rails[i].name, + psm_kobj); + if (!psm_reg_kobj[i]) { + pr_err("%s: cannot create for kobject for %s\n", + KBUILD_MODNAME, psm_rails[i].name); + rc = -ENOMEM; + goto psm_node_exit; + } + psm_rails[i].attr_gp.attrs = kzalloc( \ + sizeof(struct attribute *) * 2, GFP_KERNEL); + if (!psm_rails[i].attr_gp.attrs) { + rc = -ENOMEM; + goto psm_node_exit; + } + + PSM_RW_ATTRIB(psm_rails[i], psm_rails[i].mode_attr, 0, mode); + psm_rails[i].attr_gp.attrs[1] = NULL; + + rc = sysfs_create_group(psm_reg_kobj[i], &psm_rails[i].attr_gp); + if (rc) { + pr_err("%s: cannot create attribute group for %s\n", + KBUILD_MODNAME, psm_rails[i].name); + goto psm_node_exit; + } + } + + return rc; + +psm_node_exit: + if (rc) { + for (i = 0; i < psm_rails_cnt; i++) { + kobject_del(psm_reg_kobj[i]); + kfree(psm_rails[i].attr_gp.attrs); + } + if (psm_kobj) + kobject_del(psm_kobj); + } + return rc; +} + +static int probe_vdd_rstr(struct device_node *node, + struct msm_thermal_data *data, struct platform_device *pdev) +{ + int ret = 0; + int i = 0; + int arr_size; + char *key = NULL; + struct device_node *child_node = NULL; + + rails = NULL; + + key = "qcom,vdd-restriction-temp"; + ret = of_property_read_u32(node, key, &data->vdd_rstr_temp_degC); + if (ret) + goto read_node_fail; + + key = "qcom,vdd-restriction-temp-hysteresis"; + ret = of_property_read_u32(node, key, &data->vdd_rstr_temp_hyst_degC); + if (ret) + goto read_node_fail; + + for_each_child_of_node(node, child_node) { + rails_cnt++; + } + + if (rails_cnt == 0) + goto read_node_fail; + if (rails_cnt >= MAX_RAILS) { + pr_err("%s: Too many rails.\n", __func__); + return -EFAULT; + } + + rails = kzalloc(sizeof(struct rail) * rails_cnt, + GFP_KERNEL); + if (!rails) { + pr_err("%s: Fail to allocate memory for rails.\n", __func__); + return -ENOMEM; + } + + i = 0; + for_each_child_of_node(node, child_node) { + key = "qcom,vdd-rstr-reg"; + ret = of_property_read_string(child_node, key, &rails[i].name); + if (ret) + goto read_node_fail; + + key = "qcom,levels"; + if (!of_get_property(child_node, key, &arr_size)) + goto read_node_fail; + rails[i].num_levels = arr_size/sizeof(__be32); + if (rails[i].num_levels > + sizeof(rails[i].levels)/sizeof(uint32_t)) { + pr_err("%s: Array size too large\n", __func__); + return -EFAULT; + } + ret = of_property_read_u32_array(child_node, key, + rails[i].levels, rails[i].num_levels); + if (ret) + goto read_node_fail; + + key = "qcom,freq-req"; + rails[i].freq_req = of_property_read_bool(child_node, key); + if (rails[i].freq_req) + rails[i].min_level = 0; + else { + key = "qcom,min-level"; + ret = of_property_read_u32(child_node, key, + &rails[i].min_level); + if (ret) + goto read_node_fail; + } + + rails[i].curr_level = -1; + rails[i].reg = NULL; + i++; + } + + if (rails_cnt) { + ret = vdd_restriction_reg_init(pdev); + if (ret) { + pr_info("%s:Failed to get regulators. KTM continues.\n", + __func__); + goto read_node_fail; + } + vdd_rstr_enabled = true; + } +read_node_fail: + vdd_rstr_probed = true; + if (ret) { + dev_info(&pdev->dev, + "%s:Failed reading node=%s, key=%s. KTM continues\n", + __func__, node->full_name, key); + kfree(rails); + rails_cnt = 0; + } + if (ret == -EPROBE_DEFER) + vdd_rstr_probed = false; + return ret; +} + +static int probe_ocr(struct device_node *node, struct msm_thermal_data *data, + struct platform_device *pdev) +{ + int ret = 0; + int j = 0; + char *key = NULL; + + if (ocr_probed) { + pr_info("%s: Nodes already probed\n", + __func__); + goto read_ocr_exit; + } + ocr_rails = NULL; + + key = "qti,pmic-opt-curr-temp"; + ret = of_property_read_u32(node, key, &data->ocr_temp_degC); + if (ret) + goto read_ocr_fail; + + key = "qti,pmic-opt-curr-temp-hysteresis"; + ret = of_property_read_u32(node, key, &data->ocr_temp_hyst_degC); + if (ret) + goto read_ocr_fail; + + key = "qti,pmic-opt-curr-regs"; + ocr_rail_cnt = of_property_count_strings(node, key); + ocr_rails = kzalloc(sizeof(struct psm_rail) * ocr_rail_cnt, + GFP_KERNEL); + if (!ocr_rails) { + pr_err("%s: Fail to allocate memory for ocr rails\n", __func__); + ocr_rail_cnt = 0; + return -ENOMEM; + } + + for (j = 0; j < ocr_rail_cnt; j++) { + ret = of_property_read_string_index(node, key, j, + &ocr_rails[j].name); + if (ret) + goto read_ocr_fail; + ocr_rails[j].phase_reg = NULL; + ocr_rails[j].init = OPTIMUM_CURRENT_MAX; + } + + if (ocr_rail_cnt) { + ret = ocr_reg_init(pdev); + if (ret) { + pr_info("%s:Failed to get regulators. KTM continues.\n", + __func__); + goto read_ocr_fail; + } + ocr_enabled = true; + ocr_nodes_called = false; + /* + * Vote for max optimum current by default until we have made + * our first temp reading + */ + if (ocr_set_mode_all(OPTIMUM_CURRENT_MAX)) + pr_err("Set max optimum current failed\n"); + } + +read_ocr_fail: + ocr_probed = true; + if (ret) { + dev_info(&pdev->dev, + "%s:Failed reading node=%s, key=%s. KTM continues\n", + __func__, node->full_name, key); + if (ocr_rails) + kfree(ocr_rails); + ocr_rails = NULL; + ocr_rail_cnt = 0; + } + if (ret == -EPROBE_DEFER) + ocr_probed = false; +read_ocr_exit: + return ret; +} + +static int probe_psm(struct device_node *node, struct msm_thermal_data *data, + struct platform_device *pdev) +{ + int ret = 0; + int j = 0; + char *key = NULL; + + psm_rails = NULL; + + key = "qcom,pmic-sw-mode-temp"; + ret = of_property_read_u32(node, key, &data->psm_temp_degC); + if (ret) + goto read_node_fail; + + key = "qcom,pmic-sw-mode-temp-hysteresis"; + ret = of_property_read_u32(node, key, &data->psm_temp_hyst_degC); + if (ret) + goto read_node_fail; + + key = "qcom,pmic-sw-mode-regs"; + psm_rails_cnt = of_property_count_strings(node, key); + psm_rails = kzalloc(sizeof(struct psm_rail) * psm_rails_cnt, + GFP_KERNEL); + if (!psm_rails) { + pr_err("%s: Fail to allocate memory for psm rails\n", __func__); + psm_rails_cnt = 0; + return -ENOMEM; + } + + for (j = 0; j < psm_rails_cnt; j++) { + ret = of_property_read_string_index(node, key, j, + &psm_rails[j].name); + if (ret) + goto read_node_fail; + } + + if (psm_rails_cnt) { + ret = psm_reg_init(pdev); + if (ret) { + pr_info("%s:Failed to get regulators. KTM continues.\n", + __func__); + goto read_node_fail; + } + psm_enabled = true; + } + +read_node_fail: + psm_probed = true; + if (ret) { + dev_info(&pdev->dev, + "%s:Failed reading node=%s, key=%s. KTM continues\n", + __func__, node->full_name, key); + kfree(psm_rails); + psm_rails_cnt = 0; + } + if (ret == -EPROBE_DEFER) + psm_probed = false; + return ret; +} + +static int probe_cc(struct device_node *node, struct msm_thermal_data *data, + struct platform_device *pdev) +{ + char *key = NULL; + uint32_t cpu_cnt = 0; + int ret = 0; + uint32_t cpu = 0; + + if (num_possible_cpus() > 1) { + core_control_enabled = 1; + hotplug_enabled = 1; + } + + key = "qcom,core-limit-temp"; + ret = of_property_read_u32(node, key, &data->core_limit_temp_degC); + if (ret) + goto read_node_fail; + + key = "qcom,core-temp-hysteresis"; + ret = of_property_read_u32(node, key, &data->core_temp_hysteresis_degC); + if (ret) + goto read_node_fail; + + key = "qcom,core-control-mask"; + ret = of_property_read_u32(node, key, &data->core_control_mask); + if (ret) + goto read_node_fail; + + key = "qcom,hotplug-temp"; + ret = of_property_read_u32(node, key, &data->hotplug_temp_degC); + if (ret) + goto hotplug_node_fail; + + key = "qcom,hotplug-temp-hysteresis"; + ret = of_property_read_u32(node, key, + &data->hotplug_temp_hysteresis_degC); + if (ret) + goto hotplug_node_fail; + + key = "qcom,cpu-sensors"; + cpu_cnt = of_property_count_strings(node, key); + if (cpu_cnt < num_possible_cpus()) { + pr_err("%s: Wrong number of cpu sensors\n", KBUILD_MODNAME); + ret = -EINVAL; + goto hotplug_node_fail; + } + + for_each_possible_cpu(cpu) { + ret = of_property_read_string_index(node, key, cpu, + &cpus[cpu].sensor_type); + if (ret) + goto hotplug_node_fail; + } + +read_node_fail: + if (ret) { + dev_info(&pdev->dev, + "%s:Failed reading node=%s, key=%s. KTM continues\n", + KBUILD_MODNAME, node->full_name, key); + core_control_enabled = 0; + } + + return ret; + +hotplug_node_fail: + if (ret) { + dev_info(&pdev->dev, + "%s:Failed reading node=%s, key=%s. KTM continues\n", + KBUILD_MODNAME, node->full_name, key); + hotplug_enabled = 0; + } + + return ret; +} + +static int probe_freq_mitigation(struct device_node *node, + struct msm_thermal_data *data, + struct platform_device *pdev) +{ + char *key = NULL; + int ret = 0; + + key = "qcom,freq-mitigation-temp"; + ret = of_property_read_u32(node, key, &data->freq_mitig_temp_degc); + if (ret) + goto PROBE_FREQ_EXIT; + + key = "qcom,freq-mitigation-temp-hysteresis"; + ret = of_property_read_u32(node, key, + &data->freq_mitig_temp_hysteresis_degc); + if (ret) + goto PROBE_FREQ_EXIT; + + key = "qcom,freq-mitigation-value"; + ret = of_property_read_u32(node, key, &data->freq_limit); + if (ret) + goto PROBE_FREQ_EXIT; + + key = "qcom,freq-mitigation-control-mask"; + ret = of_property_read_u32(node, key, &data->freq_mitig_control_mask); + if (ret) + goto PROBE_FREQ_EXIT; + + freq_mitigation_enabled = 1; + +PROBE_FREQ_EXIT: + if (ret) { + dev_info(&pdev->dev, + "%s:Failed reading node=%s, key=%s. KTM continues\n", + __func__, node->full_name, key); + freq_mitigation_enabled = 0; + } + return ret; +} + +static int __devinit msm_thermal_dev_probe(struct platform_device *pdev) +{ + int ret = 0; + char *key = NULL; + struct device_node *node = pdev->dev.of_node; + struct msm_thermal_data data; + + pr_info("%s: msm_thermal_dev_probe begin...\n", KBUILD_MODNAME); + + memset(&data, 0, sizeof(struct msm_thermal_data)); + + key = "qcom,sensor-id"; + ret = of_property_read_u32(node, key, &data.sensor_id); + if (ret) + goto fail; + + key = "qcom,poll-ms"; + ret = of_property_read_u32(node, key, &data.poll_ms); + if (ret) + goto fail; + + key = "qcom,limit-temp"; + ret = of_property_read_u32(node, key, &data.limit_temp_degC); + if (ret) + goto fail; + + key = "qcom,temp-hysteresis"; + ret = of_property_read_u32(node, key, &data.temp_hysteresis_degC); + if (ret) + goto fail; + + key = "qcom,freq-step"; + ret = of_property_read_u32(node, key, &data.bootup_freq_step); + if (ret) + goto fail; + + key = "qcom,freq-control-mask"; + ret = of_property_read_u32(node, key, &data.bootup_freq_control_mask); + + ret = probe_cc(node, &data, pdev); + + ret = probe_freq_mitigation(node, &data, pdev); + /* + * Probe optional properties below. Call probe_psm before + * probe_vdd_rstr because rpm_regulator_get has to be called + * before devm_regulator_get + * probe_ocr should be called after probe_vdd_rstr to reuse the + * regualtor handle. calling devm_regulator_get more than once + * will fail. + */ + ret = probe_psm(node, &data, pdev); + if (ret == -EPROBE_DEFER) + goto fail; + ret = probe_vdd_rstr(node, &data, pdev); + if (ret == -EPROBE_DEFER) + goto fail; + ret = probe_ocr(node, &data, pdev); + if (ret == -EPROBE_DEFER) + goto fail; + + /* + * In case sysfs add nodes get called before probe function. + * Need to make sure sysfs node is created again + */ + if (psm_nodes_called) { + msm_thermal_add_psm_nodes(); + psm_nodes_called = false; + } + if (vdd_rstr_nodes_called) { + msm_thermal_add_vdd_rstr_nodes(); + vdd_rstr_nodes_called = false; + } + if (ocr_nodes_called) { + msm_thermal_add_ocr_nodes(); + ocr_nodes_called = false; + } + ret = msm_thermal_init(&data); + + pr_info("%s: msm_thermal_dev_probe completed!\n", KBUILD_MODNAME); + + return ret; +fail: + if (ret) + pr_err("%s: Failed reading node=%s, key=%s\n", + __func__, node->full_name, key); + + pr_info("%s: msm_thermal_dev_probe failed!\n", KBUILD_MODNAME); + + return ret; +} + +static int msm_thermal_dev_exit(struct platform_device *inp_dev) +{ + pr_info("msm_thermal_dev: removed!\n"); + return 0; +} + +static struct of_device_id msm_thermal_match_table[] = { + {.compatible = "qcom,msm-thermal"}, + {}, +}; + +static struct platform_driver msm_thermal_device_driver = { + .probe = msm_thermal_dev_probe, + .driver = { + .name = "msm-thermal", + .owner = THIS_MODULE, + .of_match_table = msm_thermal_match_table, + }, + .remove = msm_thermal_dev_exit, +}; + +int __init msm_thermal_device_init(void) +{ + return platform_driver_register(&msm_thermal_device_driver); +} + +int __init msm_thermal_late_init(void) +{ + if (num_possible_cpus() > 1) + msm_thermal_add_cc_nodes(); + msm_thermal_add_psm_nodes(); + msm_thermal_add_vdd_rstr_nodes(); + msm_thermal_add_ocr_nodes(); + alarm_init(&thermal_rtc, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, + thermal_rtc_callback); + INIT_WORK(&timer_work, timer_work_fn); + msm_thermal_add_timer_nodes(); + + msm_thermal_add_stat_nodes(); + return 0; +} +late_initcall(msm_thermal_late_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Praveen Chidambaram "); +MODULE_AUTHOR("Paul Reioux "); +MODULE_DESCRIPTION("intelligent thermal driver version 2 for Qualcomm based SOCs"); +MODULE_DESCRIPTION("originally from Qualcomm's open source repo"); diff --git a/include/linux/msm_thermal.h b/include/linux/msm_thermal.h index 2e3270da35f9..2071759887c9 100644 --- a/include/linux/msm_thermal.h +++ b/include/linux/msm_thermal.h @@ -34,12 +34,15 @@ struct msm_thermal_data { int32_t vdd_rstr_temp_hyst_degC; int32_t psm_temp_degC; int32_t psm_temp_hyst_degC; +#ifdef CONFIG_INTELLI_THERMAL_V2 int32_t ocr_temp_degC; int32_t ocr_temp_hyst_degC; - int32_t therm_reset_temp_degC; +#endif }; #ifdef CONFIG_THERMAL_MONITOR +#if defined(CONFIG_THERMAL_MONITOR) ||\ + defined(CONFIG_INTELLI_THERMAL_V2) extern int msm_thermal_init(struct msm_thermal_data *pdata); extern int msm_thermal_device_init(void); extern int msm_thermal_set_frequency(uint32_t cpu, uint32_t freq, From 5f568021fa568d58385f7548b3b168015db0dc38 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Sun, 7 Sep 2014 23:55:10 -0500 Subject: [PATCH 020/118] intelli-thermal v2: emulate existing behavior Signed-off-by: Paul Reioux --- drivers/thermal/intelli/msm_thermal_v2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/thermal/intelli/msm_thermal_v2.c b/drivers/thermal/intelli/msm_thermal_v2.c index af999cf71425..9644a9606a59 100644 --- a/drivers/thermal/intelli/msm_thermal_v2.c +++ b/drivers/thermal/intelli/msm_thermal_v2.c @@ -1942,6 +1942,12 @@ int __devinit msm_thermal_init(struct msm_thermal_data *pdata) if (num_possible_cpus() > 1) register_cpu_notifier(&msm_thermal_cpu_notifier); + /* emulate default behavior */ + disable_msm_thermal(); + hotplug_init(); + freq_mitigation_init(); + enabled = 0; + return ret; } From 6da96be4ed0821b3d9e0fe96ab466d121fb66050 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Mon, 8 Sep 2014 00:19:32 -0500 Subject: [PATCH 021/118] intelli-thermal v2: better debug message Signed-off-by: Paul Reioux --- drivers/thermal/intelli/msm_thermal_v2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/intelli/msm_thermal_v2.c b/drivers/thermal/intelli/msm_thermal_v2.c index 9644a9606a59..7c00e5aa51a8 100644 --- a/drivers/thermal/intelli/msm_thermal_v2.c +++ b/drivers/thermal/intelli/msm_thermal_v2.c @@ -849,7 +849,7 @@ static int set_threshold(uint32_t zone_id, long temp; if ((!threshold) || (zone_id >= max_tsens_num)) { - pr_err("%s: Invalid input\n", KBUILD_MODNAME); + pr_err("%s: Invalid input for threshold\n", KBUILD_MODNAME); ret = -EINVAL; goto set_threshold_exit; } @@ -1527,7 +1527,8 @@ int msm_thermal_set_frequency(uint32_t cpu, uint32_t freq, bool is_max) int ret = 0; if (cpu >= num_possible_cpus()) { - pr_err("%s: Invalid input\n", KBUILD_MODNAME); + pr_err("%s: Invalid input %u for frequency\n", KBUILD_MODNAME, + cpu); ret = -EINVAL; goto set_freq_exit; } From 8b9c468b5dba16351affca1a016207bccd1b0313 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Mon, 8 Sep 2014 00:27:39 -0500 Subject: [PATCH 022/118] intelli-thermal v2: even more verbose debug Signed-off-by: Paul Reioux --- drivers/thermal/intelli/msm_thermal_v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/intelli/msm_thermal_v2.c b/drivers/thermal/intelli/msm_thermal_v2.c index 7c00e5aa51a8..938c4ff4f2a2 100644 --- a/drivers/thermal/intelli/msm_thermal_v2.c +++ b/drivers/thermal/intelli/msm_thermal_v2.c @@ -849,7 +849,8 @@ static int set_threshold(uint32_t zone_id, long temp; if ((!threshold) || (zone_id >= max_tsens_num)) { - pr_err("%s: Invalid input for threshold\n", KBUILD_MODNAME); + pr_err("%s: Invalid input for threshold: zone id: %u\n", + KBUILD_MODNAME, zone_id); ret = -EINVAL; goto set_threshold_exit; } From 61f33beee52cca752144f217eb57414d3eed4dfa Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Sat, 15 Nov 2014 20:19:43 -0800 Subject: [PATCH 023/118] drivers/thermal/Kconfig: fix merge derp Signed-off-by: Paul Reioux --- drivers/thermal/Kconfig | 3 ++- include/linux/msm_thermal.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 793cb9ebda19..f66cf0b9a873 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -89,13 +89,14 @@ config THERMAL_MONITOR bool "Monitor thermal state and limit CPU Frequency" depends on THERMAL_TSENS8960 || THERMAL_TSENS8974 depends on CPU_FREQ_MSM - default n help This enables thermal monitoring capability in the kernel in the absence of a system wide thermal monitoring entity or until such an entity starts running in the userspace. Monitors TSENS temperature and limits the max frequency of the cores. +endchoice + config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on THERMAL diff --git a/include/linux/msm_thermal.h b/include/linux/msm_thermal.h index 2071759887c9..71fa6fdd5fd2 100644 --- a/include/linux/msm_thermal.h +++ b/include/linux/msm_thermal.h @@ -40,7 +40,6 @@ struct msm_thermal_data { #endif }; -#ifdef CONFIG_THERMAL_MONITOR #if defined(CONFIG_THERMAL_MONITOR) ||\ defined(CONFIG_INTELLI_THERMAL_V2) extern int msm_thermal_init(struct msm_thermal_data *pdata); From b23c8131fd35db910f209af77a4357d4e986c6b6 Mon Sep 17 00:00:00 2001 From: Myself5 Date: Mon, 20 Jul 2015 21:51:09 +0200 Subject: [PATCH 024/118] intellithermal v2: fix inline kernel building After half a year of repeating research, a 5min google search and the help of @oshmoun fixed it. Dear Makefile: Fuck You. Regards --- drivers/thermal/Makefile | 2 +- drivers/thermal/intelli/Makefile | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 drivers/thermal/intelli/Makefile diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index d7d56c205500..e967bd2d7cce 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_THERMAL_MSM_POPMEM) += msm_popmem-tm.o obj-$(CONFIG_THERMAL_TSENS) += msm_tsens.o obj-$(CONFIG_THERMAL_TSENS8960) += msm8960_tsens.o obj-$(CONFIG_THERMAL_PM8XXX) += pm8xxx-tm.o -obj-$(CONFIG_INTELLI_THERMAL_V2) += intelli/msm_thermal_v2.o +obj-$(CONFIG_INTELLI_THERMAL_V2) += intelli/ obj-$(CONFIG_THERMAL_MONITOR) += msm_thermal.o msm_thermal-dev.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_THERMAL_TSENS8974) += msm8974-tsens.o diff --git a/drivers/thermal/intelli/Makefile b/drivers/thermal/intelli/Makefile new file mode 100644 index 000000000000..5bf62f8c391e --- /dev/null +++ b/drivers/thermal/intelli/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for intelli-thermal. +# + +obj-$(CONFIG_INTELLI_THERMAL_V2) += msm_thermal_v2.o From 66f921ba3d872bbc77b214080a04c3df5de04ca5 Mon Sep 17 00:00:00 2001 From: oshmoun Date: Mon, 20 Jul 2015 22:38:07 +0200 Subject: [PATCH 025/118] enable intellithermal for rhine devices Change-Id: I8c0fcb1e00d0789b47251434a13f8f7013636e0e --- arch/arm/configs/cm_rhine_amami_row_defconfig | 4 +++- arch/arm/configs/cm_rhine_honami_row_defconfig | 4 +++- arch/arm/configs/cm_rhine_togari_row_defconfig | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/arm/configs/cm_rhine_amami_row_defconfig b/arch/arm/configs/cm_rhine_amami_row_defconfig index 793172874f9a..599f78e66464 100644 --- a/arch/arm/configs/cm_rhine_amami_row_defconfig +++ b/arch/arm/configs/cm_rhine_amami_row_defconfig @@ -385,8 +385,10 @@ CONFIG_SENSORS_EPM_ADC=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_SENSORS_QPNP_ADC_CURRENT=y CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y CONFIG_THERMAL_TSENS8974=y -CONFIG_THERMAL_MONITOR=y +CONFIG_INTELLI_THERMAL_V2=y +# CONFIG_THERMAL_MONITOR is not set CONFIG_THERMAL_QPNP=y CONFIG_THERMAL_QPNP_ADC_TM=y CONFIG_WCD9320_CODEC=y diff --git a/arch/arm/configs/cm_rhine_honami_row_defconfig b/arch/arm/configs/cm_rhine_honami_row_defconfig index 1cb879f62f88..49c406c13137 100644 --- a/arch/arm/configs/cm_rhine_honami_row_defconfig +++ b/arch/arm/configs/cm_rhine_honami_row_defconfig @@ -385,8 +385,10 @@ CONFIG_SENSORS_EPM_ADC=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_SENSORS_QPNP_ADC_CURRENT=y CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y CONFIG_THERMAL_TSENS8974=y -CONFIG_THERMAL_MONITOR=y +CONFIG_INTELLI_THERMAL_V2=y +# CONFIG_THERMAL_MONITOR is not set CONFIG_THERMAL_QPNP=y CONFIG_THERMAL_QPNP_ADC_TM=y CONFIG_WCD9320_CODEC=y diff --git a/arch/arm/configs/cm_rhine_togari_row_defconfig b/arch/arm/configs/cm_rhine_togari_row_defconfig index 3c6738b17d95..df7d28215233 100644 --- a/arch/arm/configs/cm_rhine_togari_row_defconfig +++ b/arch/arm/configs/cm_rhine_togari_row_defconfig @@ -383,8 +383,10 @@ CONFIG_SENSORS_EPM_ADC=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_SENSORS_QPNP_ADC_CURRENT=y CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y CONFIG_THERMAL_TSENS8974=y -CONFIG_THERMAL_MONITOR=y +# CONFIG_THERMAL_MONITOR is not set +CONFIG_INTELLI_THERMAL_V2=y CONFIG_THERMAL_QPNP=y CONFIG_THERMAL_QPNP_ADC_TM=y CONFIG_WCD9320_CODEC=y From 13c5b5a2a912c88838813801135854ceddde50e1 Mon Sep 17 00:00:00 2001 From: klozz Date: Sat, 28 Jun 2014 18:21:37 -0500 Subject: [PATCH 026/118] Ext4 : Increase speed --- fs/ext4/extents.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 9a66ff95d315..5f317e1fdda1 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2208,7 +2208,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); unsigned short ee_len = ext4_ext_get_actual_len(ex); ext4_fsblk_t pblk; - int flags = EXT4_FREE_BLOCKS_FORGET; + int flags = 0; if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) flags |= EXT4_FREE_BLOCKS_METADATA; @@ -2681,7 +2681,9 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, int flags = EXT4_FREE_BLOCKS_FORGET; if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - flags |= EXT4_FREE_BLOCKS_METADATA; + flags |= EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET; + else if (ext4_should_journal_data(inode)) + flags |= EXT4_FREE_BLOCKS_FORGET; ext4_free_blocks(handle, inode, NULL, EXT4_C2B(EXT4_SB(sb), partial_cluster), From dcc7e3682dd40ed295aabdec6e29d35b75f6ea24 Mon Sep 17 00:00:00 2001 From: klozz Date: Fri, 25 Jul 2014 18:16:51 -0500 Subject: [PATCH 027/118] mm: ksm: Optimize Signed-off-by: klozz --- mm/ksm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/ksm.c b/mm/ksm.c index 4d0081ac7beb..35e795d58888 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -184,10 +184,10 @@ static unsigned long ksm_pages_unshared; static unsigned long ksm_rmap_items; /* Number of pages ksmd should scan in one batch */ -static unsigned int ksm_thread_pages_to_scan = 100; +static unsigned int ksm_thread_pages_to_scan = 256; /* Milliseconds ksmd should sleep between batches */ -static unsigned int ksm_thread_sleep_millisecs = 20; +static unsigned int ksm_thread_sleep_millisecs = 1500; /* Boolean to indicate whether to use deferred timer or not */ static bool use_deferred_timer; From eea78a815e4d1506379b8c5cb20e3539a66f7d25 Mon Sep 17 00:00:00 2001 From: pec0ra Date: Wed, 14 May 2014 17:50:15 +0200 Subject: [PATCH 028/118] Add @showp1984 's msm_mpdecision Thanks to him for his work Conflicts: arch/arm/mach-msm/msm-sleeper.c arch/arm/mach-msm/msm_mpdecision.c Conflicts: arch/arm/configs/cm_shinano_sirius_defconfig Change-Id: Ia916bbca960fcfc52409e813431502325b3b3994 --- arch/arm/mach-msm/Kconfig | 8 + arch/arm/mach-msm/Makefile | 9 +- arch/arm/mach-msm/include/mach/msm_dcvs_scm.h | 29 + arch/arm/mach-msm/msm_dcvs_scm.c | 33 + arch/arm/mach-msm/msm_mpdecision.c | 1679 +++++++++++------ arch/arm/mach-msm/msm_mpdecision.h | 85 + arch/arm/mach-msm/msm_rq_stats.c | 16 + include/linux/powersuspend.h | 50 + 8 files changed, 1297 insertions(+), 612 deletions(-) create mode 100644 arch/arm/mach-msm/msm_mpdecision.h create mode 100644 include/linux/powersuspend.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 65a393740f1b..81b9415245d9 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -2017,6 +2017,14 @@ config MSM_DALRPC_TEST help Exercises DAL RPC calls to QDSP6. +config MSM_MPDEC + bool "Enable kernel based mpdecision" + depends on MSM_SMP + default n + help + This enables kernel based multi core control. + (up/down hotplug based on load) + if CPU_FREQ_MSM config MSM_CPU_FREQ_SET_MIN_MAX diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 497e78214af8..567da05714a1 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -402,7 +402,14 @@ obj-$(CONFIG_ARCH_APQ8084) += gpiomux-v2.o gpiomux.o obj-$(CONFIG_ARCH_FSM9900) += gpiomux-v2.o gpiomux.o obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o -obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_mpdecision.o +obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o + +ifdef CONFIG_MSM_MPDEC +obj-y += msm_mpdecision.o +else +obj-$(CONFIG_MSM_DCVS) += msm_mpdecision_qcom.o +endif + obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o obj-$(CONFIG_BT_MSM_PINTEST) += btpintest.o diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h b/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h index 7eefd549051b..222ba6a9bcef 100644 --- a/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h +++ b/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h @@ -23,6 +23,11 @@ enum msm_dcvs_algo_param_type { MSM_DCVS_ALGO_MPD_PARAM = 1, }; +enum msm_dcvs_algo_param_type { + MSM_DCVS_ALGO_DCVS_PARAM = 0, + MSM_DCVS_ALGO_MPD_PARAM = 1, +}; + enum msm_dcvs_scm_event { MSM_DCVS_SCM_IDLE_ENTER = 0, /* Core enters idle */ MSM_DCVS_SCM_IDLE_EXIT = 1, /* Core exits idle */ @@ -95,6 +100,18 @@ struct msm_mpd_algo_param { uint32_t slack_time_max_us; }; +struct msm_mpd_algo_param { + uint32_t em_win_size_min_us; + uint32_t em_win_size_max_us; + uint32_t em_max_util_pct; + uint32_t mp_em_rounding_point_min; + uint32_t mp_em_rounding_point_max; + uint32_t online_util_pct_min; + uint32_t online_util_pct_max; + uint32_t slack_time_min_us; + uint32_t slack_time_max_us; +}; + #ifdef CONFIG_MSM_DCVS /** * Initialize DCVS algorithm in TrustZone. @@ -164,6 +181,15 @@ extern int msm_dcvs_scm_set_power_params(uint32_t core_id, struct msm_dcvs_freq_entry *freq_entry, struct msm_dcvs_energy_curve_coeffs *coeffs); +/** + * Set MPDecision algorithm parameters + * + * @param: The param data structure + * 0 on success. + * -EINVAL: Invalid args. + */ +extern int msm_mpd_scm_set_algo_params(struct msm_mpd_algo_param *param); + /** * Do an SCM call. * @@ -252,6 +278,9 @@ static inline int msm_dcvs_set_power_params(uint32_t core_id, struct msm_dcvs_freq_entry *freq_entry, struct msm_dcvs_energy_curve_coeffs *coeffs) { return -ENOSYS; } +static inline int msm_mpd_scm_set_algo_params( + struct msm_mpd_algo_param *param) +{ return -ENOSYS; } static inline int msm_dcvs_scm_event(uint32_t core_id, enum msm_dcvs_scm_event event_id, uint32_t param0, uint32_t param1, diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c index e03ac647bd1f..7927d4e17a1b 100644 --- a/arch/arm/mach-msm/msm_dcvs_scm.c +++ b/arch/arm/mach-msm/msm_dcvs_scm.c @@ -59,6 +59,14 @@ struct msm_algo_param { } u; }; +struct msm_algo_param { + enum msm_dcvs_algo_param_type type; + union { + struct msm_dcvs_algo_param dcvs_param; + struct msm_mpd_algo_param mpd_param; + } u; +}; + int msm_dcvs_scm_init(size_t size) { int ret = 0; @@ -232,6 +240,31 @@ int msm_dcvs_scm_set_power_params(uint32_t core_id, } EXPORT_SYMBOL(msm_dcvs_scm_set_power_params); +int msm_mpd_scm_set_algo_params(struct msm_mpd_algo_param *param) +{ + int ret = 0; + struct scm_algo algo; + struct msm_algo_param *p = NULL; + + p = kzalloc(PAGE_ALIGN(sizeof(struct msm_algo_param)), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->type = MSM_DCVS_ALGO_MPD_PARAM; + memcpy(&p->u.mpd_param, param, sizeof(struct msm_mpd_algo_param)); + + algo.core_id = 0; + algo.algo_phy = virt_to_phys(p); + + ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM, + &algo, sizeof(algo), NULL, 0); + + kfree(p); + + return ret; +} +EXPORT_SYMBOL(msm_mpd_scm_set_algo_params); + int msm_dcvs_scm_event(uint32_t core_id, enum msm_dcvs_scm_event event_id, uint32_t param0, uint32_t param1, diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c index 746bbe80621c..fc6423355e22 100644 --- a/arch/arm/mach-msm/msm_mpdecision.c +++ b/arch/arm/mach-msm/msm_mpdecision.c @@ -1,726 +1,1183 @@ - /* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* + * arch/arm/mach-msm/msm_mpdecision.c + * + * This program features: + * -cpu auto-hotplug/unplug based on system load for MSM multicore cpus + * -single core while screen is off + * -extensive sysfs tuneables + * + * Copyright (c) 2012-2013, Dennis Rassmann * * 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. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * 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. + * 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. */ -#define pr_fmt(fmt) "mpd %s: " fmt, __func__ - -#include -#include +#include "msm_mpdecision.h" +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#endif +#include "acpuclock.h" #define CREATE_TRACE_POINTS #include -#define DEFAULT_RQ_AVG_POLL_MS (1) -#define DEFAULT_RQ_AVG_DIVIDE (25) - -struct mpd_attrib { - struct kobj_attribute enabled; - struct kobj_attribute rq_avg_poll_ms; - struct kobj_attribute iowait_threshold_pct; - - struct kobj_attribute rq_avg_divide; - struct kobj_attribute em_win_size_min_us; - struct kobj_attribute em_win_size_max_us; - struct kobj_attribute em_max_util_pct; - struct kobj_attribute mp_em_rounding_point_min; - struct kobj_attribute mp_em_rounding_point_max; - struct kobj_attribute online_util_pct_min; - struct kobj_attribute online_util_pct_max; - struct kobj_attribute slack_time_min_us; - struct kobj_attribute slack_time_max_us; - struct kobj_attribute hp_up_max_ms; - struct kobj_attribute hp_up_ms; - struct kobj_attribute hp_up_count; - struct kobj_attribute hp_dw_max_ms; - struct kobj_attribute hp_dw_ms; - struct kobj_attribute hp_dw_count; - struct attribute_group attrib_group; +#define DEBUG 1 + +DEFINE_PER_CPU(struct msm_mpdec_cpudata_t, msm_mpdec_cpudata); +EXPORT_PER_CPU_SYMBOL_GPL(msm_mpdec_cpudata); + +static bool mpdec_suspended = false; +static struct delayed_work msm_mpdec_work; +static struct workqueue_struct *msm_mpdec_workq; +static DEFINE_MUTEX(mpdec_msm_cpu_lock); +static DEFINE_MUTEX(mpdec_msm_susres_lock); +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +static struct workqueue_struct *mpdec_input_wq; +static DEFINE_PER_CPU(struct work_struct, mpdec_input_work); +static struct workqueue_struct *msm_mpdec_revib_workq; +static DEFINE_PER_CPU(struct delayed_work, msm_mpdec_revib_work); +#endif + +static struct msm_mpdec_tuners { + unsigned int startdelay; + unsigned int delay; + unsigned int pause; + bool scroff_single_core; + unsigned long int idle_freq; + unsigned int max_cpus; + unsigned int min_cpus; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + bool boost_enabled; + unsigned int boost_time; + unsigned long int boost_freq[4]; +#endif +} msm_mpdec_tuners_ins = { + .startdelay = MSM_MPDEC_STARTDELAY, + .delay = MSM_MPDEC_DELAY, + .pause = MSM_MPDEC_PAUSE, + .scroff_single_core = true, + .idle_freq = MSM_MPDEC_IDLE_FREQ, + .max_cpus = CONFIG_NR_CPUS, + .min_cpus = 1, +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + .boost_enabled = true, + .boost_time = MSM_MPDEC_BOOSTTIME, + .boost_freq = { + MSM_MPDEC_BOOSTFREQ_CPU0, + MSM_MPDEC_BOOSTFREQ_CPU1, + MSM_MPDEC_BOOSTFREQ_CPU2, + MSM_MPDEC_BOOSTFREQ_CPU3 + }, +#endif }; -struct msm_mpd_scm_data { - enum msm_dcvs_scm_event event; - int nr; -}; +static unsigned int NwNs_Threshold[8] = {12, 0, 20, 7, 25, 10, 0, 18}; +static unsigned int TwTs_Threshold[8] = {140, 0, 140, 190, 140, 190, 0, 190}; -struct mpdecision { - uint32_t enabled; - atomic_t algo_cpu_mask; - uint32_t rq_avg_poll_ms; - uint32_t iowait_threshold_pct; - uint32_t rq_avg_divide; - ktime_t next_update; - uint32_t slack_us; - struct msm_mpd_algo_param mp_param; - struct mpd_attrib attrib; - struct mutex lock; - struct task_struct *task; - struct task_struct *hptask; - struct hrtimer slack_timer; - struct msm_mpd_scm_data data; - int hpupdate; - wait_queue_head_t wait_q; - wait_queue_head_t wait_hpq; -}; +extern unsigned int get_rq_info(void); +extern unsigned long acpuclk_get_rate(int); -struct hp_latency { - int hp_up_max_ms; - int hp_up_ms; - int hp_up_count; - int hp_dw_max_ms; - int hp_dw_ms; - int hp_dw_count; -}; +unsigned int state = MSM_MPDEC_IDLE; +bool was_paused = false; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +bool is_screen_on = true; +static int update_cpu_min_freq(struct cpufreq_policy *cpu_policy, + int cpu, int new_freq); +static void unboost_cpu(int cpu); +#endif +static cputime64_t mpdec_paused_until = 0; -static DEFINE_PER_CPU(struct hrtimer, rq_avg_poll_timer); -static DEFINE_SPINLOCK(rq_avg_lock); +static unsigned long get_rate(int cpu) { + return acpuclk_get_rate(cpu); +} -enum { - MSM_MPD_DEBUG_NOTIFIER = BIT(0), - MSM_MPD_CORE_STATUS = BIT(1), - MSM_MPD_SLACK_TIMER = BIT(2), -}; +static int get_slowest_cpu(void) { + int i, cpu = 0; + unsigned long rate, slow_rate = 0; + + for (i = 1; i < CONFIG_NR_CPUS; i++) { + if (!cpu_online(i)) + continue; + rate = get_rate(i); + if (slow_rate == 0) { + cpu = i; + slow_rate = rate; + continue; + } + if ((rate <= slow_rate) && (slow_rate != 0)) { + cpu = i; + slow_rate = rate; + } + } -enum { - HPUPDATE_WAITING = 0, /* we are waiting for cpumask update */ - HPUPDATE_SCHEDULED = 1, /* we are in the process of hotplugging */ - HPUPDATE_IN_PROGRESS = 2, /* we are in the process of hotplugging */ -}; + return cpu; +} -static int msm_mpd_enabled = 1; -module_param_named(enabled, msm_mpd_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP); +static unsigned long get_slowest_cpu_rate(void) { + int i = 0; + unsigned long rate, slow_rate = 0; + + for (i = 0; i < CONFIG_NR_CPUS; i++) { + if (!cpu_online(i)) + continue; + rate = get_rate(i); + if ((rate < slow_rate) && (slow_rate != 0)) { + slow_rate = rate; + continue; + } + if (slow_rate == 0) { + slow_rate = rate; + } + } + + return slow_rate; +} + +static void mpdec_cpu_up(int cpu) { + if (!cpu_online(cpu)) { + mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex); + cpu_up(cpu); + per_cpu(msm_mpdec_cpudata, cpu).on_time = ktime_to_ms(ktime_get()); + per_cpu(msm_mpdec_cpudata, cpu).online = true; + per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged += 1; + pr_info(MPDEC_TAG"CPU[%d] off->on | Mask=[%d%d%d%d]\n", + cpu, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3)); + mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex); + } +} +EXPORT_SYMBOL_GPL(mpdec_cpu_up); + +static void mpdec_cpu_down(int cpu) { + cputime64_t on_time = 0; + if (cpu_online(cpu)) { + mutex_lock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex); + cpu_down(cpu); + on_time = (ktime_to_ms(ktime_get()) - per_cpu(msm_mpdec_cpudata, cpu).on_time); + per_cpu(msm_mpdec_cpudata, cpu).online = false; + per_cpu(msm_mpdec_cpudata, cpu).on_time_total += on_time; + per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged += 1; + pr_info(MPDEC_TAG"CPU[%d] on->off | Mask=[%d%d%d%d] | time online: %llu\n", + cpu, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3), on_time); + mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex); + } +} +EXPORT_SYMBOL_GPL(mpdec_cpu_down); + +static int mp_decision(void) { + static bool first_call = true; + int new_state = MSM_MPDEC_IDLE; + int nr_cpu_online; + int index; + unsigned int rq_depth; + static cputime64_t total_time = 0; + static cputime64_t last_time; + cputime64_t current_time; + cputime64_t this_time = 0; + + if (state == MSM_MPDEC_DISABLED) + return MSM_MPDEC_DISABLED; + + current_time = ktime_to_ms(ktime_get()); + + if (first_call) { + first_call = false; + } else { + this_time = current_time - last_time; + } + total_time += this_time; + + rq_depth = get_rq_info(); + nr_cpu_online = num_online_cpus(); + + if (nr_cpu_online) { + index = (nr_cpu_online - 1) * 2; + if ((nr_cpu_online < CONFIG_NR_CPUS) && (rq_depth >= NwNs_Threshold[index])) { + if ((total_time >= TwTs_Threshold[index]) && + (nr_cpu_online < msm_mpdec_tuners_ins.max_cpus)) { + new_state = MSM_MPDEC_UP; + pr_info(MPDEC_TAG"Slowest cpu rate : %lu\n", get_slowest_cpu_rate()); + // TODO : Temporarilly disabled idle freq because get_slowest_cpu_rate doesn't work. +// if (get_slowest_cpu_rate() <= msm_mpdec_tuners_ins.idle_freq) +// new_state = MSM_MPDEC_IDLE; + } + } else if ((nr_cpu_online > 1) && (rq_depth <= NwNs_Threshold[index+1])) { + if ((total_time >= TwTs_Threshold[index+1]) && + (nr_cpu_online > msm_mpdec_tuners_ins.min_cpus)) { + new_state = MSM_MPDEC_DOWN; + if (get_slowest_cpu_rate() > msm_mpdec_tuners_ins.idle_freq) + new_state = MSM_MPDEC_IDLE; + } + } else { + new_state = MSM_MPDEC_IDLE; + total_time = 0; + } + } else { + total_time = 0; + } -static struct dentry *debugfs_base; -static struct mpdecision msm_mpd; + if (new_state != MSM_MPDEC_IDLE) { + total_time = 0; + } -static struct hp_latency hp_latencies; + last_time = ktime_to_ms(ktime_get()); +#if DEBUG + pr_info(MPDEC_TAG"[DEBUG] rq: %u, new_state: %i | Mask=[%d%d%d%d]\n", + rq_depth, new_state, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3)); +#endif + return new_state; +} -static unsigned long last_nr; -static int num_present_hundreds; -static ktime_t last_down_time; +static void msm_mpdec_work_thread(struct work_struct *work) { + unsigned int cpu = nr_cpu_ids; -static bool ok_to_update_tz(int nr, int last_nr) -{ - /* - * Exclude unnecessary TZ reports if run queue haven't changed much from - * the last reported value. The divison by rq_avg_divide is to - * filter out small changes in the run queue average which won't cause - * a online cpu mask change. Also if the cpu online count does not match - * the count requested by TZ and we are not in the process of bringing - * cpus online as indicated by a HPUPDATE_IN_PROGRESS in msm_mpd.hpdata - */ - return - (((nr / msm_mpd.rq_avg_divide) - != (last_nr / msm_mpd.rq_avg_divide)) - || ((hweight32(atomic_read(&msm_mpd.algo_cpu_mask)) - != num_online_cpus()) - && (msm_mpd.hpupdate != HPUPDATE_IN_PROGRESS))); -} - -static enum hrtimer_restart msm_mpd_rq_avg_poll_timer(struct hrtimer *timer) -{ - int nr, nr_iowait; - ktime_t curr_time = ktime_get(); - unsigned long flags; - int cpu = smp_processor_id(); - enum hrtimer_restart restart = HRTIMER_RESTART; + /* Check if we are paused */ + if (mpdec_paused_until >= ktime_to_ms(ktime_get())) + goto out; - spin_lock_irqsave(&rq_avg_lock, flags); - /* If running on the wrong cpu, don't restart */ - if (&per_cpu(rq_avg_poll_timer, cpu) != timer) - restart = HRTIMER_NORESTART; + if (mpdec_suspended == true) + goto out; - if (ktime_to_ns(ktime_sub(curr_time, msm_mpd.next_update)) < 0) + if (!mutex_trylock(&mpdec_msm_cpu_lock)) goto out; - msm_mpd.next_update = ktime_add_ns(curr_time, - (msm_mpd.rq_avg_poll_ms * NSEC_PER_MSEC)); + /* if sth messed with the cpus, update the check vars so we can proceed */ + if (was_paused) { + for_each_possible_cpu(cpu) { + if (cpu_online(cpu)) + per_cpu(msm_mpdec_cpudata, cpu).online = true; + else if (!cpu_online(cpu)) + per_cpu(msm_mpdec_cpudata, cpu).online = false; + } + was_paused = false; + } + + state = mp_decision(); + switch (state) { + case MSM_MPDEC_DISABLED: + case MSM_MPDEC_IDLE: + break; + case MSM_MPDEC_DOWN: + cpu = get_slowest_cpu(); + if (cpu < nr_cpu_ids) { + if ((per_cpu(msm_mpdec_cpudata, cpu).online == true) && (cpu_online(cpu))) { +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + unboost_cpu(cpu); +#endif + mpdec_cpu_down(cpu); + } else if (per_cpu(msm_mpdec_cpudata, cpu).online != cpu_online(cpu)) { + pr_info(MPDEC_TAG"CPU[%d] was controlled outside of mpdecision! | pausing [%d]ms\n", + cpu, msm_mpdec_tuners_ins.pause); + mpdec_paused_until = ktime_to_ms(ktime_get()) + msm_mpdec_tuners_ins.pause; + was_paused = true; + } + } + break; + case MSM_MPDEC_UP: + cpu = cpumask_next_zero(0, cpu_online_mask); + pr_info(MPDEC_TAG"We try to put cpu %d up", cpu); + if (cpu < nr_cpu_ids) { + if ((per_cpu(msm_mpdec_cpudata, cpu).online == false) && (!cpu_online(cpu))) { + mpdec_cpu_up(cpu); +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + unboost_cpu(cpu); +#endif + } else if (per_cpu(msm_mpdec_cpudata, cpu).online != cpu_online(cpu)) { + pr_info(MPDEC_TAG"CPU[%d] was controlled outside of mpdecision! | pausing [%d]ms\n", + cpu, msm_mpdec_tuners_ins.pause); + mpdec_paused_until = ktime_to_ms(ktime_get()) + msm_mpdec_tuners_ins.pause; + was_paused = true; + } + } + break; + default: + pr_err(MPDEC_TAG"%s: invalid mpdec hotplug state %d\n", + __func__, state); + } + mutex_unlock(&mpdec_msm_cpu_lock); + +out: + if (state != MSM_MPDEC_DISABLED) + queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work, + msecs_to_jiffies(msm_mpdec_tuners_ins.delay)); + return; +} + +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +static int update_cpu_min_freq(struct cpufreq_policy *cpu_policy, + int cpu, int new_freq) { + int ret = 0; - sched_get_nr_running_avg(&nr, &nr_iowait); + if (!cpu_policy) + return -EINVAL; - if ((nr_iowait >= msm_mpd.iowait_threshold_pct) && (nr < last_nr)) - nr = last_nr; + cpufreq_verify_within_limits(cpu_policy, new_freq, cpu_policy->max); + cpu_policy->user_policy.min = new_freq; - if (nr > num_present_hundreds) - nr = num_present_hundreds; + ret = cpufreq_update_policy(cpu); + if (!ret) { + pr_debug(MPDEC_TAG"Touch event! Setting CPU%d min frequency to %d\n", + cpu, new_freq); + } + return ret; +} - trace_msm_mp_runq("nr_running", nr); +static void unboost_cpu(int cpu) { + struct cpufreq_policy *cpu_policy = NULL; - if (ok_to_update_tz(nr, last_nr)) { - hrtimer_try_to_cancel(&msm_mpd.slack_timer); - msm_mpd.data.nr = nr; - msm_mpd.data.event = MSM_DCVS_SCM_RUNQ_UPDATE; - wake_up(&msm_mpd.wait_q); - last_nr = nr; + if (cpu_online(cpu)) { + if (per_cpu(msm_mpdec_cpudata, cpu).is_boosted) { + if (mutex_trylock(&per_cpu(msm_mpdec_cpudata, cpu).unboost_mutex)) { + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) { + pr_debug(MPDEC_TAG"NULL policy on cpu %d\n", cpu); + return; + } +#if DEBUG + pr_info(MPDEC_TAG"un boosted cpu%i to %lu", cpu, per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq); +#endif + per_cpu(msm_mpdec_cpudata, cpu).is_boosted = false; + per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = false; + if ((cpu_policy->min != per_cpu(msm_mpdec_cpudata, cpu).boost_freq) && + (cpu_policy->min != per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq)) { + pr_info(MPDEC_TAG"cpu%u min was changed while boosted (%lu->%u), using new min", + cpu, per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq, cpu_policy->min); + per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq = cpu_policy->min; + } + update_cpu_min_freq(cpu_policy, cpu, per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq); + cpufreq_cpu_put(cpu_policy); + mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).unboost_mutex); + } + } } -out: - hrtimer_set_expires(timer, msm_mpd.next_update); - spin_unlock_irqrestore(&rq_avg_lock, flags); - /* set next expiration */ - return restart; + return; } -static void bring_up_cpu(int cpu) -{ - int cpu_action_time_ms; - int time_taken_ms; - int ret, ret1, ret2; - - cpu_action_time_ms = ktime_to_ms(ktime_get()); - ret = cpu_up(cpu); - if (ret) { - pr_debug("Error %d online core %d\n", ret, cpu); +static void msm_mpdec_revib_work_thread(struct work_struct *work) { + int cpu = smp_processor_id(); + + if (per_cpu(msm_mpdec_cpudata, cpu).is_boosted) { + per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = true; + if (ktime_to_ms(ktime_get()) > per_cpu(msm_mpdec_cpudata, cpu).boost_until) { + unboost_cpu(cpu); + } else { + queue_delayed_work_on( + cpu, + msm_mpdec_revib_workq, + &per_cpu(msm_mpdec_revib_work, cpu), + msecs_to_jiffies((per_cpu(msm_mpdec_cpudata, cpu).boost_until - ktime_to_ms(ktime_get()))) + ); + } } else { - time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms; - if (time_taken_ms > hp_latencies.hp_up_max_ms) - hp_latencies.hp_up_max_ms = time_taken_ms; - hp_latencies.hp_up_ms += time_taken_ms; - hp_latencies.hp_up_count++; - ret = msm_dcvs_scm_event( - CPU_OFFSET + cpu, - MSM_DCVS_SCM_CORE_ONLINE, - cpufreq_get(cpu), - (uint32_t) time_taken_ms * USEC_PER_MSEC, - &ret1, &ret2); - if (ret) - pr_err("Error sending hotplug scm event err=%d\n", ret); - } -} - -static void bring_down_cpu(int cpu) -{ - int cpu_action_time_ms; - int time_taken_ms; - int ret, ret1, ret2; - - BUG_ON(cpu == 0); - cpu_action_time_ms = ktime_to_ms(ktime_get()); - ret = cpu_down(cpu); - if (ret) { - pr_debug("Error %d offline" "core %d\n", ret, cpu); + per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = false; + } + return; +} + +static void mpdec_input_callback(struct work_struct *unused) { + struct cpufreq_policy *cpu_policy = NULL; + int cpu = smp_processor_id(); + bool boosted = false; + + if (!per_cpu(msm_mpdec_cpudata, cpu).is_boosted) { + if (mutex_trylock(&per_cpu(msm_mpdec_cpudata, cpu).boost_mutex)) { + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) { + pr_debug(MPDEC_TAG"NULL policy on cpu %d\n", cpu); + return; + } + per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq = cpu_policy->min; + + /* check if boost freq is > minfreq */ + cpufreq_verify_within_limits(cpu_policy, cpu_policy->min, per_cpu(msm_mpdec_cpudata, cpu).boost_freq); + + update_cpu_min_freq(cpu_policy, cpu, per_cpu(msm_mpdec_cpudata, cpu).boost_freq); +#if DEBUG + pr_info(MPDEC_TAG"boosted cpu%i to %lu", cpu, per_cpu(msm_mpdec_cpudata, cpu).boost_freq); +#endif + per_cpu(msm_mpdec_cpudata, cpu).is_boosted = true; + per_cpu(msm_mpdec_cpudata, cpu).boost_until = ktime_to_ms(ktime_get()) + msm_mpdec_tuners_ins.boost_time; + boosted = true; + cpufreq_cpu_put(cpu_policy); + mutex_unlock(&per_cpu(msm_mpdec_cpudata, cpu).boost_mutex); + } } else { - time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms; - if (time_taken_ms > hp_latencies.hp_dw_max_ms) - hp_latencies.hp_dw_max_ms = time_taken_ms; - hp_latencies.hp_dw_ms += time_taken_ms; - hp_latencies.hp_dw_count++; - ret = msm_dcvs_scm_event( - CPU_OFFSET + cpu, - MSM_DCVS_SCM_CORE_OFFLINE, - (uint32_t) time_taken_ms * USEC_PER_MSEC, - 0, - &ret1, &ret2); - if (ret) - pr_err("Error sending hotplug scm event err=%d\n", ret); - } -} - -static int __ref msm_mpd_update_scm(enum msm_dcvs_scm_event event, int nr) -{ - int ret = 0; - uint32_t req_cpu_mask = 0; - uint32_t slack_us = 0; - uint32_t param0 = 0; + boosted = true; + } + if (boosted && !per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running) { + per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = true; + queue_delayed_work_on( + cpu, + msm_mpdec_revib_workq, + &per_cpu(msm_mpdec_revib_work, cpu), + msecs_to_jiffies(msm_mpdec_tuners_ins.boost_time) + ); + } else if (boosted && per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running) { + per_cpu(msm_mpdec_cpudata, cpu).boost_until = ktime_to_ms(ktime_get()) + msm_mpdec_tuners_ins.boost_time; + } + + return; +} + +#ifdef CONFIG_BRICKED_THERMAL +extern int bricked_thermal_throttled; +#endif + +static void mpdec_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) { + int i = 0; + +#ifdef CONFIG_BRICKED_THERMAL + if (bricked_thermal_throttled > 0) + return; +#endif - if (event == MSM_DCVS_SCM_RUNQ_UPDATE) - param0 = nr; + if (!msm_mpdec_tuners_ins.boost_enabled) + return; - ret = msm_dcvs_scm_event(0, event, param0, 0, - &req_cpu_mask, &slack_us); + if (!is_screen_on) + return; - if (ret) { - pr_err("Error (%d) sending event %d, param %d\n", ret, event, - param0); - return ret; + for_each_online_cpu(i) { + queue_work_on(i, mpdec_input_wq, &per_cpu(mpdec_input_work, i)); } +} + +static int input_dev_filter(const char *input_dev_name) { + if (strstr(input_dev_name, "touch") || + strstr(input_dev_name, "key") || + strstr(input_dev_name, "power") || + strstr(input_dev_name, "pwr") || + strstr(input_dev_name, "lid")) { + return 0; + } else { + return 1; + } +} + +static int mpdec_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) { + struct input_handle *handle; + int error; + + if (input_dev_filter(dev->name)) + return -ENODEV; - trace_msm_mp_cpusonline("cpu_online_mp", req_cpu_mask); - trace_msm_mp_slacktime("slack_time_mp", slack_us); - msm_mpd.slack_us = slack_us; - atomic_set(&msm_mpd.algo_cpu_mask, req_cpu_mask); - msm_mpd.hpupdate = HPUPDATE_SCHEDULED; - wake_up(&msm_mpd.wait_hpq); + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "mpdec"; + + 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 mpdec_input_disconnect(struct input_handle *handle) { + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} - /* Start MP Decision slack timer */ - if (slack_us) { - hrtimer_cancel(&msm_mpd.slack_timer); - ret = hrtimer_start(&msm_mpd.slack_timer, - ktime_set(0, slack_us * NSEC_PER_USEC), - HRTIMER_MODE_REL_PINNED); - if (ret) - pr_err("Failed to register slack timer (%d) %d\n", - slack_us, ret); +static const struct input_device_id mpdec_ids[] = { + { .driver_info = 1 }, + { }, +}; + +static struct input_handler mpdec_input_handler = { + .event = mpdec_input_event, + .connect = mpdec_input_connect, + .disconnect = mpdec_input_disconnect, + .name = "mpdec_inputreq", + .id_table = mpdec_ids, +}; +#endif + +static void msm_mpdec_suspend(struct work_struct * msm_mpdec_suspend_work) { + int cpu = nr_cpu_ids; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + is_screen_on = false; +#endif + + if (!msm_mpdec_tuners_ins.scroff_single_core) { + pr_info(MPDEC_TAG"Screen -> off\n"); + return; } - return ret; + /* main work thread can sleep now */ + cancel_delayed_work_sync(&msm_mpdec_work); + + for_each_possible_cpu(cpu) { +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + unboost_cpu(cpu); +#endif + if ((cpu >= 1) && (cpu_online(cpu))) { + mpdec_cpu_down(cpu); + } + } + mpdec_suspended = true; + + pr_info(MPDEC_TAG"Screen -> off. Deactivated mpdecision.\n"); } +static DECLARE_WORK(msm_mpdec_suspend_work, msm_mpdec_suspend); + +static void msm_mpdec_resume(struct work_struct * msm_mpdec_suspend_work) { + int cpu = nr_cpu_ids; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + is_screen_on = true; +#endif + + if (!mpdec_suspended) + return; + + mpdec_suspended = false; + + if (msm_mpdec_tuners_ins.scroff_single_core) { + /* wake up main work thread */ + was_paused = true; + queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work, 0); + /* restore min/max cpus limits */ + for (cpu=1; cpu msm_mpdec_tuners_ins.max_cpus) { + if (cpu_online(cpu)) + mpdec_cpu_down(cpu); + } + } + pr_info(MPDEC_TAG"Screen -> on. Activated mpdecision. | Mask=[%d%d%d%d]\n", + cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3)); + } else { + pr_info(MPDEC_TAG"Screen -> on\n"); + } +} +static DECLARE_WORK(msm_mpdec_resume_work, msm_mpdec_resume); -static enum hrtimer_restart msm_mpd_slack_timer(struct hrtimer *timer) -{ - unsigned long flags; +static void msm_mpdec_early_suspend(struct power_suspend *h) { + mutex_lock(&mpdec_msm_susres_lock); + schedule_work(&msm_mpdec_suspend_work); + mutex_unlock(&mpdec_msm_susres_lock); +} - trace_printk("mpd:slack_timer_fired!\n"); +static void msm_mpdec_late_resume(struct power_suspend *h) { + mutex_lock(&mpdec_msm_susres_lock); + schedule_work(&msm_mpdec_resume_work); + mutex_unlock(&mpdec_msm_susres_lock); +} - spin_lock_irqsave(&rq_avg_lock, flags); - if (msm_mpd.data.event == MSM_DCVS_SCM_RUNQ_UPDATE) - goto out; +static struct power_suspend msm_mpdec_early_suspend_handler = { + .suspend = msm_mpdec_early_suspend, + .resume = msm_mpdec_late_resume, +}; - msm_mpd.data.nr = 0; - msm_mpd.data.event = MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED; - wake_up(&msm_mpd.wait_q); -out: - spin_unlock_irqrestore(&rq_avg_lock, flags); - return HRTIMER_NORESTART; +/**************************** SYSFS START ****************************/ +struct kobject *msm_mpdec_kobject; + +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", msm_mpdec_tuners_ins.object); \ } -static int msm_mpd_idle_notifier(struct notifier_block *self, - unsigned long cmd, void *v) +show_one(startdelay, startdelay); +show_one(delay, delay); +show_one(pause, pause); +show_one(scroff_single_core, scroff_single_core); +show_one(min_cpus, min_cpus); +show_one(max_cpus, max_cpus); +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +show_one(boost_enabled, boost_enabled); +show_one(boost_time, boost_time); +#endif + +#define show_one_twts(file_name, arraypos) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", TwTs_Threshold[arraypos]); \ +} +show_one_twts(twts_threshold_0, 0); +show_one_twts(twts_threshold_1, 1); +show_one_twts(twts_threshold_2, 2); +show_one_twts(twts_threshold_3, 3); +show_one_twts(twts_threshold_4, 4); +show_one_twts(twts_threshold_5, 5); +show_one_twts(twts_threshold_6, 6); +show_one_twts(twts_threshold_7, 7); + +#define store_one_twts(file_name, arraypos) \ +static ssize_t store_##file_name \ +(struct kobject *a, struct attribute *b, const char *buf, size_t count) \ +{ \ + unsigned int input; \ + int ret; \ + ret = sscanf(buf, "%u", &input); \ + if (ret != 1) \ + return -EINVAL; \ + TwTs_Threshold[arraypos] = input; \ + return count; \ +} \ +define_one_global_rw(file_name); +store_one_twts(twts_threshold_0, 0); +store_one_twts(twts_threshold_1, 1); +store_one_twts(twts_threshold_2, 2); +store_one_twts(twts_threshold_3, 3); +store_one_twts(twts_threshold_4, 4); +store_one_twts(twts_threshold_5, 5); +store_one_twts(twts_threshold_6, 6); +store_one_twts(twts_threshold_7, 7); + +#define show_one_nwns(file_name, arraypos) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", NwNs_Threshold[arraypos]); \ +} +show_one_nwns(nwns_threshold_0, 0); +show_one_nwns(nwns_threshold_1, 1); +show_one_nwns(nwns_threshold_2, 2); +show_one_nwns(nwns_threshold_3, 3); +show_one_nwns(nwns_threshold_4, 4); +show_one_nwns(nwns_threshold_5, 5); +show_one_nwns(nwns_threshold_6, 6); +show_one_nwns(nwns_threshold_7, 7); + +#define store_one_nwns(file_name, arraypos) \ +static ssize_t store_##file_name \ +(struct kobject *a, struct attribute *b, const char *buf, size_t count) \ +{ \ + unsigned int input; \ + int ret; \ + ret = sscanf(buf, "%u", &input); \ + if (ret != 1) \ + return -EINVAL; \ + NwNs_Threshold[arraypos] = input; \ + return count; \ +} \ +define_one_global_rw(file_name); +store_one_nwns(nwns_threshold_0, 0); +store_one_nwns(nwns_threshold_1, 1); +store_one_nwns(nwns_threshold_2, 2); +store_one_nwns(nwns_threshold_3, 3); +store_one_nwns(nwns_threshold_4, 4); +store_one_nwns(nwns_threshold_5, 5); +store_one_nwns(nwns_threshold_6, 6); +store_one_nwns(nwns_threshold_7, 7); + +static ssize_t show_idle_freq (struct kobject *kobj, struct attribute *attr, + char *buf) { - int cpu = smp_processor_id(); - unsigned long flags; - - switch (cmd) { - case CPU_PM_EXIT: - spin_lock_irqsave(&rq_avg_lock, flags); - hrtimer_start(&per_cpu(rq_avg_poll_timer, cpu), - msm_mpd.next_update, - HRTIMER_MODE_ABS_PINNED); - spin_unlock_irqrestore(&rq_avg_lock, flags); + return sprintf(buf, "%lu\n", msm_mpdec_tuners_ins.idle_freq); +} + +static ssize_t show_enabled(struct kobject *a, struct attribute *b, + char *buf) +{ + unsigned int enabled; + switch (state) { + case MSM_MPDEC_DISABLED: + enabled = 0; break; - case CPU_PM_ENTER: - hrtimer_cancel(&per_cpu(rq_avg_poll_timer, cpu)); + case MSM_MPDEC_IDLE: + case MSM_MPDEC_DOWN: + case MSM_MPDEC_UP: + enabled = 1; break; default: - break; + enabled = 333; } + return sprintf(buf, "%u\n", enabled); +} + +static ssize_t store_startdelay(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; - return NOTIFY_OK; + msm_mpdec_tuners_ins.startdelay = input; + + return count; } -static int msm_mpd_hotplug_notifier(struct notifier_block *self, - unsigned long action, void *hcpu) +static ssize_t store_delay(struct kobject *a, struct attribute *b, + const char *buf, size_t count) { - int cpu = (int)hcpu; - unsigned long flags; - - switch (action & (~CPU_TASKS_FROZEN)) { - case CPU_STARTING: - spin_lock_irqsave(&rq_avg_lock, flags); - hrtimer_start(&per_cpu(rq_avg_poll_timer, cpu), - msm_mpd.next_update, - HRTIMER_MODE_ABS_PINNED); - spin_unlock_irqrestore(&rq_avg_lock, flags); - break; - default: - break; - } + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; - return NOTIFY_OK; + msm_mpdec_tuners_ins.delay = input; + + return count; } -static struct notifier_block msm_mpd_idle_nb = { - .notifier_call = msm_mpd_idle_notifier, -}; +static ssize_t store_pause(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; -static struct notifier_block msm_mpd_hotplug_nb = { - .notifier_call = msm_mpd_hotplug_notifier, -}; + msm_mpdec_tuners_ins.pause = input; -static int __cpuinit msm_mpd_do_hotplug(void *data) + return count; +} + +static ssize_t store_idle_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) { - int *event = (int *)data; - int cpu; + long unsigned int input; + int ret; + ret = sscanf(buf, "%lu", &input); + if (ret != 1) + return -EINVAL; + + msm_mpdec_tuners_ins.idle_freq = input; - while (1) { - msm_dcvs_update_algo_params(); - wait_event(msm_mpd.wait_hpq, *event || kthread_should_stop()); - if (kthread_should_stop()) + return count; +} + +static ssize_t store_scroff_single_core(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + switch (buf[0]) { + case '0': + case '1': + msm_mpdec_tuners_ins.scroff_single_core = input; break; + default: + ret = -EINVAL; + } + return count; +} - msm_mpd.hpupdate = HPUPDATE_IN_PROGRESS; - /* - * Bring online any offline cores, then offline any online - * cores. Whenever a core is off/onlined restart the procedure - * in case a new core is desired to be brought online in the - * mean time. - */ -restart: - for_each_possible_cpu(cpu) { - if ((atomic_read(&msm_mpd.algo_cpu_mask) & (1 << cpu)) - && !cpu_online(cpu)) { - bring_up_cpu(cpu); - if (cpu_online(cpu)) - goto restart; - } +static ssize_t store_max_cpus(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret, cpu; + ret = sscanf(buf, "%u", &input); + if ((ret != 1) || input > CONFIG_NR_CPUS || input < msm_mpdec_tuners_ins.min_cpus) + return -EINVAL; + + msm_mpdec_tuners_ins.max_cpus = input; + if (num_online_cpus() > input) { + for (cpu=CONFIG_NR_CPUS; cpu>0; cpu--) { + if (num_online_cpus() <= input) + break; + if (!cpu_online(cpu)) + continue; + mpdec_cpu_down(cpu); } - - if (ktime_to_ns(ktime_sub(ktime_get(), last_down_time)) > - 100 * NSEC_PER_MSEC) - for_each_possible_cpu(cpu) - if (!(atomic_read(&msm_mpd.algo_cpu_mask) & - (1 << cpu)) && cpu_online(cpu)) { - bring_down_cpu(cpu); - last_down_time = ktime_get(); - break; - } - msm_mpd.hpupdate = HPUPDATE_WAITING; - msm_dcvs_apply_gpu_floor(0); + pr_info(MPDEC_TAG"max_cpus set to %u. Affected CPUs were unplugged!\n", input); } - return 0; + return count; } -static int msm_mpd_do_update_scm(void *data) +static ssize_t store_min_cpus(struct kobject *a, struct attribute *b, + const char *buf, size_t count) { - struct msm_mpd_scm_data *scm_data = (struct msm_mpd_scm_data *)data; - unsigned long flags; - enum msm_dcvs_scm_event event; - int nr; - - while (1) { - wait_event(msm_mpd.wait_q, - msm_mpd.data.event == MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED - || msm_mpd.data.event == MSM_DCVS_SCM_RUNQ_UPDATE - || kthread_should_stop()); - - if (kthread_should_stop()) - break; - - spin_lock_irqsave(&rq_avg_lock, flags); - event = scm_data->event; - nr = scm_data->nr; - scm_data->event = 0; - scm_data->nr = 0; - spin_unlock_irqrestore(&rq_avg_lock, flags); + unsigned int input; + int ret, cpu; + ret = sscanf(buf, "%u", &input); + if ((ret != 1) || input < 1 || input > msm_mpdec_tuners_ins.max_cpus) + return -EINVAL; - msm_mpd_update_scm(event, nr); + msm_mpdec_tuners_ins.min_cpus = input; + if (num_online_cpus() < input) { + for (cpu=1; cpu= input) + break; + if (cpu_online(cpu)) + continue; + mpdec_cpu_up(cpu); + } + pr_info(MPDEC_TAG"min_cpus set to %u. Affected CPUs were hotplugged!\n", input); } - return 0; + + return count; } -static int __ref msm_mpd_set_enabled(uint32_t enable) +static ssize_t store_enabled(struct kobject *a, struct attribute *b, + const char *buf, size_t count) { - int ret = 0; - int ret0 = 0; - int ret1 = 0; - int cpu; - static uint32_t last_enable; - - enable = (enable > 0) ? 1 : 0; - if (last_enable == enable) - return ret; - - if (enable) { - ret = msm_mpd_scm_set_algo_params(&msm_mpd.mp_param); - if (ret) { - pr_err("Error(%d): msm_mpd_scm_set_algo_params failed\n", - ret); - return ret; - } + unsigned int cpu, input, enabled; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + switch (state) { + case MSM_MPDEC_DISABLED: + enabled = 0; + break; + case MSM_MPDEC_IDLE: + case MSM_MPDEC_DOWN: + case MSM_MPDEC_UP: + enabled = 1; + break; + default: + enabled = 333; } - ret = msm_dcvs_scm_event(0, MSM_DCVS_SCM_MPD_ENABLE, enable, 0, - &ret0, &ret1); - if (ret) { - pr_err("Error(%d) %s MP Decision\n", - ret, (enable ? "enabling" : "disabling")); - } else { - last_enable = enable; - last_nr = 0; - } - if (enable) { - msm_mpd.next_update = ktime_add_ns(ktime_get(), - (msm_mpd.rq_avg_poll_ms * NSEC_PER_MSEC)); - msm_mpd.task = kthread_run(msm_mpd_do_update_scm, - &msm_mpd.data, "msm_mpdecision"); - if (IS_ERR(msm_mpd.task)) - return -EFAULT; - - msm_mpd.hptask = kthread_run(msm_mpd_do_hotplug, - &msm_mpd.hpupdate, "msm_hp"); - if (IS_ERR(msm_mpd.hptask)) - return -EFAULT; - - for_each_online_cpu(cpu) - hrtimer_start(&per_cpu(rq_avg_poll_timer, cpu), - msm_mpd.next_update, - HRTIMER_MODE_ABS_PINNED); - cpu_pm_register_notifier(&msm_mpd_idle_nb); - register_cpu_notifier(&msm_mpd_hotplug_nb); - msm_mpd.enabled = 1; - } else { - for_each_online_cpu(cpu) - hrtimer_cancel(&per_cpu(rq_avg_poll_timer, cpu)); - kthread_stop(msm_mpd.hptask); - kthread_stop(msm_mpd.task); - cpu_pm_unregister_notifier(&msm_mpd_idle_nb); - unregister_cpu_notifier(&msm_mpd_hotplug_nb); - msm_mpd.enabled = 0; + if (buf[0] == enabled) + return -EINVAL; + + switch (buf[0]) { + case '0': + state = MSM_MPDEC_DISABLED; + pr_info(MPDEC_TAG"nap time... Hot plugging offline CPUs...\n"); + for (cpu = 1; cpu < CONFIG_NR_CPUS; cpu++) + if (!cpu_online(cpu)) + mpdec_cpu_up(cpu); + break; + case '1': + state = MSM_MPDEC_IDLE; + was_paused = true; + queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work, + msecs_to_jiffies(msm_mpdec_tuners_ins.delay)); + pr_info(MPDEC_TAG"firing up mpdecision...\n"); + break; + default: + ret = -EINVAL; } + return count; +} - return ret; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +static ssize_t store_boost_enabled(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + msm_mpdec_tuners_ins.boost_enabled = input; + + return count; } -static int msm_mpd_set_rq_avg_poll_ms(uint32_t val) +static ssize_t store_boost_time(struct kobject *a, struct attribute *b, + const char *buf, size_t count) { - /* - * No need to do anything. Just let the timer set its own next poll - * interval when it next fires. - */ - msm_mpd.rq_avg_poll_ms = val; - return 0; + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + msm_mpdec_tuners_ins.boost_time = input; + + return count; } -static int msm_mpd_set_iowait_threshold_pct(uint32_t val) +static ssize_t show_boost_freqs(struct kobject *a, struct attribute *b, + char *buf) { - /* - * No need to do anything. Just let the timer set its own next poll - * interval when it next fires. - */ - msm_mpd.iowait_threshold_pct = val; - return 0; + ssize_t len = 0; + int cpu = 0; + + for_each_present_cpu(cpu) { + len += sprintf(buf + len, "%lu\n", per_cpu(msm_mpdec_cpudata, cpu).boost_freq); + } + return len; } +static ssize_t store_boost_freqs(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int i = 0; + unsigned int cpu = 0; + long unsigned int hz = 0; + const char *chz = NULL; + + for (i=0; idev.platform_data; - - module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); - if (!module_kobj) { - pr_err("Cannot find kobject for module %s\n", KBUILD_MODNAME); - ret = -ENOENT; - goto done; - } - - msm_mpd.attrib.attrib_group.attrs = - kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL); - if (!msm_mpd.attrib.attrib_group.attrs) { - ret = -ENOMEM; - goto done; - } - - MPD_RW_ATTRIB(0, enabled); - MPD_RW_ATTRIB(1, rq_avg_poll_ms); - MPD_RW_ATTRIB(2, iowait_threshold_pct); - MPD_RW_ATTRIB(3, rq_avg_divide); - MPD_RW_ATTRIB(4, em_win_size_min_us); - MPD_RW_ATTRIB(5, em_win_size_max_us); - MPD_RW_ATTRIB(6, em_max_util_pct); - MPD_RW_ATTRIB(7, mp_em_rounding_point_min); - MPD_RW_ATTRIB(8, mp_em_rounding_point_max); - MPD_RW_ATTRIB(9, online_util_pct_min); - MPD_RW_ATTRIB(10, online_util_pct_max); - MPD_RW_ATTRIB(11, slack_time_min_us); - MPD_RW_ATTRIB(12, slack_time_max_us); - MPD_RW_ATTRIB(13, hp_up_max_ms); - MPD_RW_ATTRIB(14, hp_up_ms); - MPD_RW_ATTRIB(15, hp_up_count); - MPD_RW_ATTRIB(16, hp_dw_max_ms); - MPD_RW_ATTRIB(17, hp_dw_ms); - MPD_RW_ATTRIB(18, hp_dw_count); - - msm_mpd.attrib.attrib_group.attrs[19] = NULL; - ret = sysfs_create_group(module_kobj, &msm_mpd.attrib.attrib_group); - if (ret) - pr_err("Unable to create sysfs objects :%d\n", ret); - - msm_mpd.rq_avg_poll_ms = DEFAULT_RQ_AVG_POLL_MS; - msm_mpd.rq_avg_divide = DEFAULT_RQ_AVG_DIVIDE; - - memcpy(&msm_mpd.mp_param, param, sizeof(struct msm_mpd_algo_param)); - - debugfs_base = debugfs_create_dir("msm_mpdecision", NULL); - if (!debugfs_base) { - pr_err("Cannot create debugfs base msm_mpdecision\n"); - ret = -ENOENT; - goto done; - } - -done: - if (ret && debugfs_base) - debugfs_remove(debugfs_base); + ssize_t len = 0; + int cpu = 0; - return ret; + for_each_possible_cpu(cpu) { + len += sprintf(buf + len, "%i %llu\n", cpu, per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged); + } + + return len; } +define_one_global_ro(times_cpus_hotplugged); -static int __devexit msm_mpd_remove(struct platform_device *pdev) +static ssize_t show_times_cpus_unplugged(struct kobject *a, struct attribute *b, + char *buf) { - platform_set_drvdata(pdev, NULL); + ssize_t len = 0; + int cpu = 0; - return 0; + for_each_possible_cpu(cpu) { + len += sprintf(buf + len, "%i %llu\n", cpu, per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged); + } + + return len; } +define_one_global_ro(times_cpus_unplugged); -static struct platform_driver msm_mpd_driver = { - .probe = msm_mpd_probe, - .remove = __devexit_p(msm_mpd_remove), - .driver = { - .name = "msm_mpdecision", - .owner = THIS_MODULE, - }, +static struct attribute *msm_mpdec_stats_attributes[] = { + &time_cpus_on.attr, + ×_cpus_hotplugged.attr, + ×_cpus_unplugged.attr, + NULL }; -static int __init msm_mpdecision_init(void) -{ - int cpu; - if (!msm_mpd_enabled) { - pr_info("Not enabled\n"); - return 0; - } - num_present_hundreds = 100 * num_present_cpus(); +static struct attribute_group msm_mpdec_stats_attr_group = { + .attrs = msm_mpdec_stats_attributes, + .name = "stats", +}; +/**************************** SYSFS END ****************************/ - hrtimer_init(&msm_mpd.slack_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_PINNED); - msm_mpd.slack_timer.function = msm_mpd_slack_timer; +static int __init msm_mpdec_init(void) { + int cpu, rc, err = 0; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + int i; + unsigned long int boost_freq = 0; +#endif + mpdec_suspended = false; for_each_possible_cpu(cpu) { - hrtimer_init(&per_cpu(rq_avg_poll_timer, cpu), - CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED); - per_cpu(rq_avg_poll_timer, cpu).function - = msm_mpd_rq_avg_poll_timer; + mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).hotplug_mutex)); + per_cpu(msm_mpdec_cpudata, cpu).online = true; + per_cpu(msm_mpdec_cpudata, cpu).on_time_total = 0; + per_cpu(msm_mpdec_cpudata, cpu).times_cpu_unplugged = 0; + per_cpu(msm_mpdec_cpudata, cpu).times_cpu_hotplugged = 0; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + per_cpu(msm_mpdec_cpudata, cpu).norm_min_freq = CONFIG_MSM_CPU_FREQ_MIN; + switch (cpu) { + case 0: + case 1: + case 2: + boost_freq = msm_mpdec_tuners_ins.boost_freq[cpu]; + break; + default: + boost_freq = msm_mpdec_tuners_ins.boost_freq[3]; + break; + } + per_cpu(msm_mpdec_cpudata, cpu).boost_freq = boost_freq; + per_cpu(msm_mpdec_cpudata, cpu).is_boosted = false; + per_cpu(msm_mpdec_cpudata, cpu).revib_wq_running = false; + per_cpu(msm_mpdec_cpudata, cpu).boost_until = 0; + mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).boost_mutex)); + mutex_init(&(per_cpu(msm_mpdec_cpudata, cpu).unboost_mutex)); +#endif + } + + was_paused = true; + + msm_mpdec_workq = alloc_workqueue( + "mpdec", + WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, + 1 + ); + if (!msm_mpdec_workq) + return -ENOMEM; + INIT_DELAYED_WORK(&msm_mpdec_work, msm_mpdec_work_thread); + +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + mpdec_input_wq = create_workqueue("mpdeciwq"); + if (!mpdec_input_wq) { + printk(KERN_ERR "%s: Failed to create mpdeciwq workqueue\n", __func__); + return -EFAULT; + } + msm_mpdec_revib_workq = create_workqueue("mpdecribwq"); + if (!msm_mpdec_revib_workq) { + printk(KERN_ERR "%s: Failed to create mpdecrevibwq workqueue\n", __func__); + return -EFAULT; + } + for_each_possible_cpu(i) { + INIT_WORK(&per_cpu(mpdec_input_work, i), mpdec_input_callback); + INIT_DELAYED_WORK(&per_cpu(msm_mpdec_revib_work, i), msm_mpdec_revib_work_thread); } - mutex_init(&msm_mpd.lock); - init_waitqueue_head(&msm_mpd.wait_q); - init_waitqueue_head(&msm_mpd.wait_hpq); - return platform_driver_register(&msm_mpd_driver); + rc = input_register_handler(&mpdec_input_handler); +#endif + + if (state != MSM_MPDEC_DISABLED) + queue_delayed_work(msm_mpdec_workq, &msm_mpdec_work, + msecs_to_jiffies(msm_mpdec_tuners_ins.startdelay)); + + msm_mpdec_kobject = kobject_create_and_add("msm_mpdecision", kernel_kobj); + if (msm_mpdec_kobject) { + rc = sysfs_create_group(msm_mpdec_kobject, + &msm_mpdec_attr_group); + if (rc) { + pr_warn(MPDEC_TAG"sysfs: ERROR, could not create sysfs group"); + } + rc = sysfs_create_group(msm_mpdec_kobject, + &msm_mpdec_stats_attr_group); + if (rc) { + pr_warn(MPDEC_TAG"sysfs: ERROR, could not create sysfs stats group"); + } + } else + pr_warn(MPDEC_TAG"sysfs: ERROR, could not create sysfs kobj"); + + pr_info(MPDEC_TAG"%s init complete.", __func__); + + + register_power_suspend(&msm_mpdec_early_suspend_handler); + + return err; +} +late_initcall(msm_mpdec_init); + +void msm_mpdec_exit(void) { +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + input_unregister_handler(&mpdec_input_handler); + destroy_workqueue(msm_mpdec_revib_workq); + destroy_workqueue(mpdec_input_wq); +#endif + destroy_workqueue(msm_mpdec_workq); } -late_initcall(msm_mpdecision_init); diff --git a/arch/arm/mach-msm/msm_mpdecision.h b/arch/arm/mach-msm/msm_mpdecision.h new file mode 100644 index 000000000000..6b47a58e9628 --- /dev/null +++ b/arch/arm/mach-msm/msm_mpdecision.h @@ -0,0 +1,85 @@ +/* + * arch/arm/mach-msm/msm_mpdecision.h + * + * Copyright (c) 2012-2013, Dennis Rassmann + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __MSM_MPDEC_H__ +#define __MSM_MPDEC_H__ + +#include +#include + +#define MPDEC_TAG "[MPDEC]: " +#define MSM_MPDEC_STARTDELAY 20000 +#define MSM_MPDEC_DELAY 130 +#define MSM_MPDEC_PAUSE 10000 + +#ifdef CONFIG_ARCH_MSM8974 +#define MSM_MPDEC_IDLE_FREQ 422400 +#elif defined CONFIG_ARCH_MSM8X60 || defined CONFIG_ARCH_MSM8960 || defined CONFIG_ARCH_MSM8930 +#define MSM_MPDEC_IDLE_FREQ 486000 +#else +#define MSM_MPDEC_IDLE_FREQ 486000 +#endif + +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN +#define MSM_MPDEC_BOOSTTIME 1000 +#ifdef CONFIG_ARCH_MSM8974 +#define MSM_MPDEC_BOOSTFREQ_CPU0 960000 +#define MSM_MPDEC_BOOSTFREQ_CPU1 960000 +#define MSM_MPDEC_BOOSTFREQ_CPU2 729600 +#define MSM_MPDEC_BOOSTFREQ_CPU3 576000 +#elif defined CONFIG_ARCH_MSM8X60 || defined CONFIG_ARCH_MSM8960 || defined CONFIG_ARCH_MSM8930 +#define MSM_MPDEC_BOOSTFREQ_CPU0 918000 +#define MSM_MPDEC_BOOSTFREQ_CPU1 918000 +#define MSM_MPDEC_BOOSTFREQ_CPU2 702000 +#define MSM_MPDEC_BOOSTFREQ_CPU3 594000 +#else +#define MSM_MPDEC_BOOSTFREQ_CPU0 918000 +#define MSM_MPDEC_BOOSTFREQ_CPU1 918000 +#define MSM_MPDEC_BOOSTFREQ_CPU2 702000 +#define MSM_MPDEC_BOOSTFREQ_CPU3 594000 +#endif +#endif + +enum { + MSM_MPDEC_DISABLED = 0, + MSM_MPDEC_IDLE, + MSM_MPDEC_DOWN, + MSM_MPDEC_UP, +}; + +struct msm_mpdec_cpudata_t { + struct mutex hotplug_mutex; + int online; + cputime64_t on_time; + cputime64_t on_time_total; + long long unsigned int times_cpu_hotplugged; + long long unsigned int times_cpu_unplugged; +#ifdef CONFIG_MSM_MPDEC_INPUTBOOST_CPUMIN + struct mutex boost_mutex; + struct mutex unboost_mutex; + unsigned long int norm_min_freq; + unsigned long int boost_freq; + cputime64_t boost_until; + bool is_boosted; + bool revib_wq_running; +#endif +}; +#endif //__MSM_MPDEC_H__ + diff --git a/arch/arm/mach-msm/msm_rq_stats.c b/arch/arm/mach-msm/msm_rq_stats.c index af4a6d23d70b..590b1b7a27b4 100644 --- a/arch/arm/mach-msm/msm_rq_stats.c +++ b/arch/arm/mach-msm/msm_rq_stats.c @@ -236,6 +236,22 @@ static ssize_t hotplug_disable_show(struct kobject *kobj, static struct kobj_attribute hotplug_disabled_attr = __ATTR_RO(hotplug_disable); +unsigned int get_rq_info(void) +{ +unsigned long flags = 0; + unsigned int rq = 0; + + spin_lock_irqsave(&rq_lock, flags); + + rq = rq_info.rq_avg; + rq_info.rq_avg = 0; + + spin_unlock_irqrestore(&rq_lock, flags); + + return rq; +} +EXPORT_SYMBOL(get_rq_info); + static void def_work_fn(struct work_struct *work) { int64_t diff; diff --git a/include/linux/powersuspend.h b/include/linux/powersuspend.h new file mode 100644 index 000000000000..a78677d6615a --- /dev/null +++ b/include/linux/powersuspend.h @@ -0,0 +1,50 @@ +/* include/linux/powersuspend.h + * + * Copyright (C) 2007-2008 Google, Inc. + * Copyright (C) 2013 Paul Reioux + * + * Modified by Jean-Pierre Rasquin + * + * v1.1 - make powersuspend not depend on a userspace initiator anymore, + * but use a hook in autosleep instead. + * + * v1.2 - make kernel / userspace mode switchable + * + * v1.3 - add a hook in display pannel driver as alternative kernel trigger + * + * 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. + * + */ + +#ifndef _LINUX_POWERSUSPEND_H +#define _LINUX_POWERSUSPEND_H + +#include + +#define POWER_SUSPEND_INACTIVE 0 +#define POWER_SUSPEND_ACTIVE 1 + +#define POWER_SUSPEND_AUTOSLEEP 0 // Use kernel autosleep as hook +#define POWER_SUSPEND_USERSPACE 1 // Use fauxclock as trigger +#define POWER_SUSPEND_PANNEL 2 // Use display pannel state as hook + +struct power_suspend { + struct list_head link; + void (*suspend)(struct power_suspend *h); + void (*resume)(struct power_suspend *h); +}; + +void register_power_suspend(struct power_suspend *handler); +void unregister_power_suspend(struct power_suspend *handler); + +void set_power_suspend_state_autosleep_hook(int new_state); +void set_power_suspend_state_pannel_hook(int new_state); + +#endif From 767ee8a757913e0286a61a63d9ddc35115bf05f1 Mon Sep 17 00:00:00 2001 From: Saatvik Shukla Date: Mon, 27 Jul 2015 23:46:34 +0530 Subject: [PATCH 029/118] update defconfig Change-Id: Id4d39bb3e8b9da418890725dd0833a562473ffff Conflicts: arch/arm/configs/cm_rhine_togari_row_defconfig --- arch/arm/configs/cm_rhine_togari_row_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/cm_rhine_togari_row_defconfig b/arch/arm/configs/cm_rhine_togari_row_defconfig index df7d28215233..b369684d4396 100644 --- a/arch/arm/configs/cm_rhine_togari_row_defconfig +++ b/arch/arm/configs/cm_rhine_togari_row_defconfig @@ -616,3 +616,4 @@ CONFIG_CRYPTO_DEV_QCEDEV=y #BlackSheep CONFIG_FB_MSM_MDSS_KCAL_CTRL=y CONFIG_SOUND_CONTROL_HAX_3_GPL=y +MSM_MPDEC=y \ No newline at end of file From 999785aa0a254e43a7e3e617164170968f57e68f Mon Sep 17 00:00:00 2001 From: Venkat kamesh Date: Tue, 12 May 2015 01:33:20 +0530 Subject: [PATCH 030/118] Implement and enable Quickwakeup driver Conflicts: arch/arm/configs/z1_defconfig arch/arm/configs/z1c_defconfig Change-Id: Idec891a3406d96e1eb9b5502f102586deffab3ce --- .../arm/configs/cm_rhine_togari_row_defconfig | 1 + include/linux/quickwakeup.h | 47 ++++++++ kernel/power/Kconfig | 7 ++ kernel/power/Makefile | 1 + kernel/power/quickwakeup.c | 105 ++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 include/linux/quickwakeup.h create mode 100644 kernel/power/quickwakeup.c diff --git a/arch/arm/configs/cm_rhine_togari_row_defconfig b/arch/arm/configs/cm_rhine_togari_row_defconfig index b369684d4396..f0b3b21e6b9a 100644 --- a/arch/arm/configs/cm_rhine_togari_row_defconfig +++ b/arch/arm/configs/cm_rhine_togari_row_defconfig @@ -615,5 +615,6 @@ CONFIG_CRYPTO_DEV_QCEDEV=y #BlackSheep CONFIG_FB_MSM_MDSS_KCAL_CTRL=y +CONFIG_QUICK_WAKEUP=y CONFIG_SOUND_CONTROL_HAX_3_GPL=y MSM_MPDEC=y \ No newline at end of file diff --git a/include/linux/quickwakeup.h b/include/linux/quickwakeup.h new file mode 100644 index 000000000000..07a49388ef12 --- /dev/null +++ b/include/linux/quickwakeup.h @@ -0,0 +1,47 @@ +/* include/linux/quickwakeup.h + * + * Copyright (C) 2014 Motorola Mobility LLC. + * + * 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. + * + */ + +#ifndef _QUICKWAKEUP_H_ +#define _QUICKWAKEUP_H_ + +#ifdef __KERNEL__ + +struct quickwakeup_ops { + struct list_head list; + char *name; + int (*qw_execute)(void *data); + int (*qw_check)(void *data); + int execute; + void *data; /* arbitrary data passed back to user */ +}; + +#ifdef CONFIG_QUICK_WAKEUP + +int quickwakeup_register(struct quickwakeup_ops *ops); +void quickwakeup_unregister(struct quickwakeup_ops *ops); +bool quickwakeup_suspend_again(void); + +#else + +static inline int quickwakeup_register(struct quickwakeup_ops *ops) { return 0; }; +static inline void quickwakeup_unregister(struct quickwakeup_ops *ops) {}; +static inline bool quickwakeup_suspend_again(void) { return 0; }; + +#endif /* CONFIG_QUICK_WAKEUP */ + +#endif /* __KERNEL__ */ + +#endif /* _QUICKWAKEUP_H_ */ + diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 1a5c6ecda27e..f6e3385a1c7f 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -289,3 +289,10 @@ config SUSPEND_TIME Prints the time spent in suspend in the kernel log, and keeps statistics on the time spent in suspend in /sys/kernel/debug/suspend_time +config QUICK_WAKEUP + bool "Quick wakeup" + depends on SUSPEND + default n + ---help--- + Allow kernel driver to do periodic jobs without resuming the full system + This option can increase battery life on android powered smartphone. diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 29472bff11ef..06251455470e 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o +obj-$(CONFIG_QUICK_WAKEUP) += quickwakeup.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/quickwakeup.c b/kernel/power/quickwakeup.c new file mode 100644 index 000000000000..cbea7be50ea4 --- /dev/null +++ b/kernel/power/quickwakeup.c @@ -0,0 +1,105 @@ +/* kernel/power/quickwakeup.c + * + * Copyright (C) 2014 Motorola Mobility LLC. + * + * 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 +#include +#include +#include + +static LIST_HEAD(qw_head); +static DEFINE_MUTEX(list_lock); + +int quickwakeup_register(struct quickwakeup_ops *ops) +{ + mutex_lock(&list_lock); + list_add(&ops->list, &qw_head); + mutex_unlock(&list_lock); + + return 0; +} + +void quickwakeup_unregister(struct quickwakeup_ops *ops) +{ + mutex_lock(&list_lock); + list_del(&ops->list); + mutex_unlock(&list_lock); +} + +static int quickwakeup_check(void) +{ + int check = 0; + struct quickwakeup_ops *index; + + mutex_lock(&list_lock); + + list_for_each_entry(index, &qw_head, list) { + int ret = index->qw_check(index->data); + index->execute = ret; + check |= ret; + pr_debug("%s: %s votes for %s\n", __func__, index->name, + ret ? "execute" : "dont care"); + } + + mutex_unlock(&list_lock); + + return check; +} + +/* return 1 => suspend again + return 0 => continue wakeup + */ +static int quickwakeup_execute(void) +{ + int suspend_again = 0; + int final_vote = 1; + struct quickwakeup_ops *index; + + mutex_lock(&list_lock); + + list_for_each_entry(index, &qw_head, list) { + if (index->execute) { + int ret = index->qw_execute(index->data); + index->execute = 0; + final_vote &= ret; + suspend_again = final_vote; + pr_debug("%s: %s votes for %s\n", __func__, index->name, + ret ? "suspend again" : "wakeup"); + } + } + + mutex_unlock(&list_lock); + + pr_debug("%s: %s\n", __func__, + suspend_again ? "suspend again" : "wakeup"); + + return suspend_again; +} + +/* return 1 => suspend again + return 0 => continue wakeup + */ +bool quickwakeup_suspend_again(void) +{ + int ret = 0; + + if (quickwakeup_check()) + ret = quickwakeup_execute(); + + pr_debug("%s- returning %d %s\n", __func__, ret, + ret ? "suspend again" : "wakeup"); + + return ret; +} + From 4b5e997137a84fba741afa4ad4addfe664d1c4c5 Mon Sep 17 00:00:00 2001 From: FlyFrog Date: Wed, 4 Sep 2013 12:45:44 +0700 Subject: [PATCH 031/118] int_sqrt: Improve 3x faster integer sqrt. Result on 10,000,000 call. Old: sqrt(12345689) = 3513 real 0m0.768s user 0m0.760s sys 0m0.004s New: sqrt(12345689) = 3513 real 0m0.222s user 0m0.224s sys 0m0.000s Signed-off-by: engstk Conflicts: lib/int_sqrt.c Change-Id: Iaa9f0b64c85ea05687de2326f7308eb6fff28204 --- lib/int_sqrt.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/int_sqrt.c b/lib/int_sqrt.c index fc2eeb7cb2ea..6d6912139abf 100644 --- a/lib/int_sqrt.c +++ b/lib/int_sqrt.c @@ -8,9 +8,12 @@ * * A very rough approximation to the sqrt() function. */ -unsigned long int_sqrt(unsigned long x) +inline unsigned long int_sqrt(unsigned long x) { unsigned long op, res, one; + register unsigned long tmp; + register unsigned long place; + register unsigned long root = 0; op = x; res = 0; @@ -28,5 +31,24 @@ unsigned long int_sqrt(unsigned long x) one /= 4; } return res; + place = 1UL << (BITS_PER_LONG - 2); + + do{ + place >>= 2; + }while(place > x); + + do { + tmp = root + place; + root >>= 1; + + if (x >= tmp) + { + x -= tmp; + root += place; + } + place >>= 2; + }while (place != 0); + + return root; } EXPORT_SYMBOL(int_sqrt); From b673574370e36788ab1719c9d33a80ecd8e8b4f0 Mon Sep 17 00:00:00 2001 From: Francis Date: Mon, 18 Aug 2014 20:06:20 +0300 Subject: [PATCH 032/118] Linux 3.4.91 Conflicts: Makefile --- arch/mips/power/hibernate.S | 1 + arch/powerpc/lib/crtsavres.S | 186 +++++++++++++ arch/x86/crypto/ghash-clmulni-intel_asm.S | 28 -- arch/x86/crypto/ghash-clmulni-intel_glue.c | 14 +- block/blk-core.c | 2 +- drivers/ata/libata-core.c | 21 +- drivers/block/floppy.c | 11 +- drivers/gpio/gpio-mxs.c | 3 +- drivers/gpu/drm/i915/intel_crt.c | 8 + drivers/md/dm-thin.c | 2 +- drivers/mtd/nand/nuc900_nand.c | 2 +- drivers/mtd/sm_ftl.c | 11 +- drivers/net/wireless/b43/phy_n.c | 14 +- drivers/net/wireless/rtlwifi/rtl8192cu/hw.c | 15 +- drivers/net/wireless/rtlwifi/rtl8192se/hw.c | 27 +- drivers/scsi/megaraid/megaraid_mm.c | 2 + drivers/scsi/mpt2sas/mpt2sas_scsih.c | 1 - drivers/tty/hvc/hvc_console.c | 2 +- drivers/video/aty/mach64_accel.c | 3 +- drivers/video/aty/mach64_cursor.c | 22 +- drivers/video/cfbcopyarea.c | 153 +++++------ drivers/video/matrox/matroxfb_accel.c | 38 ++- drivers/video/matrox/matroxfb_base.h | 2 + drivers/video/tgafb.c | 279 +++++--------------- drivers/virtio/virtio_balloon.c | 6 + fs/locks.c | 7 +- include/linux/libata.h | 1 + include/linux/net.h | 23 ++ include/trace/events/block.h | 33 ++- kernel/trace/blktrace.c | 20 +- kernel/tracepoint.c | 6 + net/bridge/netfilter/ebtables.c | 5 +- net/ipv4/netfilter/arp_tables.c | 6 +- net/ipv4/netfilter/ip_tables.c | 6 +- net/ipv6/netfilter/ip6_tables.c | 6 +- scripts/mod/modpost.c | 8 +- 36 files changed, 569 insertions(+), 405 deletions(-) diff --git a/arch/mips/power/hibernate.S b/arch/mips/power/hibernate.S index f8a751c03282..5bf34ec89669 100644 --- a/arch/mips/power/hibernate.S +++ b/arch/mips/power/hibernate.S @@ -44,6 +44,7 @@ LEAF(swsusp_arch_resume) bne t1, t3, 1b PTR_L t0, PBE_NEXT(t0) bnez t0, 0b + jal local_flush_tlb_all /* Avoid TLB mismatch after kernel resume */ PTR_LA t0, saved_regs PTR_L ra, PT_R31(t0) PTR_L sp, PT_R29(t0) diff --git a/arch/powerpc/lib/crtsavres.S b/arch/powerpc/lib/crtsavres.S index 1c893f05d224..21ecdf5e55f9 100644 --- a/arch/powerpc/lib/crtsavres.S +++ b/arch/powerpc/lib/crtsavres.S @@ -230,6 +230,87 @@ _GLOBAL(_rest32gpr_31_x) mr 1,11 blr +#ifdef CONFIG_ALTIVEC +/* Called with r0 pointing just beyond the end of the vector save area. */ + +_GLOBAL(_savevr_20) + li r11,-192 + stvx vr20,r11,r0 +_GLOBAL(_savevr_21) + li r11,-176 + stvx vr21,r11,r0 +_GLOBAL(_savevr_22) + li r11,-160 + stvx vr22,r11,r0 +_GLOBAL(_savevr_23) + li r11,-144 + stvx vr23,r11,r0 +_GLOBAL(_savevr_24) + li r11,-128 + stvx vr24,r11,r0 +_GLOBAL(_savevr_25) + li r11,-112 + stvx vr25,r11,r0 +_GLOBAL(_savevr_26) + li r11,-96 + stvx vr26,r11,r0 +_GLOBAL(_savevr_27) + li r11,-80 + stvx vr27,r11,r0 +_GLOBAL(_savevr_28) + li r11,-64 + stvx vr28,r11,r0 +_GLOBAL(_savevr_29) + li r11,-48 + stvx vr29,r11,r0 +_GLOBAL(_savevr_30) + li r11,-32 + stvx vr30,r11,r0 +_GLOBAL(_savevr_31) + li r11,-16 + stvx vr31,r11,r0 + blr + +_GLOBAL(_restvr_20) + li r11,-192 + lvx vr20,r11,r0 +_GLOBAL(_restvr_21) + li r11,-176 + lvx vr21,r11,r0 +_GLOBAL(_restvr_22) + li r11,-160 + lvx vr22,r11,r0 +_GLOBAL(_restvr_23) + li r11,-144 + lvx vr23,r11,r0 +_GLOBAL(_restvr_24) + li r11,-128 + lvx vr24,r11,r0 +_GLOBAL(_restvr_25) + li r11,-112 + lvx vr25,r11,r0 +_GLOBAL(_restvr_26) + li r11,-96 + lvx vr26,r11,r0 +_GLOBAL(_restvr_27) + li r11,-80 + lvx vr27,r11,r0 +_GLOBAL(_restvr_28) + li r11,-64 + lvx vr28,r11,r0 +_GLOBAL(_restvr_29) + li r11,-48 + lvx vr29,r11,r0 +_GLOBAL(_restvr_30) + li r11,-32 + lvx vr30,r11,r0 +_GLOBAL(_restvr_31) + li r11,-16 + lvx vr31,r11,r0 + blr + +#endif /* CONFIG_ALTIVEC */ + #else /* CONFIG_PPC64 */ .globl _savegpr0_14 @@ -353,6 +434,111 @@ _restgpr0_31: mtlr r0 blr +#ifdef CONFIG_ALTIVEC +/* Called with r0 pointing just beyond the end of the vector save area. */ + +.globl _savevr_20 +_savevr_20: + li r12,-192 + stvx vr20,r12,r0 +.globl _savevr_21 +_savevr_21: + li r12,-176 + stvx vr21,r12,r0 +.globl _savevr_22 +_savevr_22: + li r12,-160 + stvx vr22,r12,r0 +.globl _savevr_23 +_savevr_23: + li r12,-144 + stvx vr23,r12,r0 +.globl _savevr_24 +_savevr_24: + li r12,-128 + stvx vr24,r12,r0 +.globl _savevr_25 +_savevr_25: + li r12,-112 + stvx vr25,r12,r0 +.globl _savevr_26 +_savevr_26: + li r12,-96 + stvx vr26,r12,r0 +.globl _savevr_27 +_savevr_27: + li r12,-80 + stvx vr27,r12,r0 +.globl _savevr_28 +_savevr_28: + li r12,-64 + stvx vr28,r12,r0 +.globl _savevr_29 +_savevr_29: + li r12,-48 + stvx vr29,r12,r0 +.globl _savevr_30 +_savevr_30: + li r12,-32 + stvx vr30,r12,r0 +.globl _savevr_31 +_savevr_31: + li r12,-16 + stvx vr31,r12,r0 + blr + +.globl _restvr_20 +_restvr_20: + li r12,-192 + lvx vr20,r12,r0 +.globl _restvr_21 +_restvr_21: + li r12,-176 + lvx vr21,r12,r0 +.globl _restvr_22 +_restvr_22: + li r12,-160 + lvx vr22,r12,r0 +.globl _restvr_23 +_restvr_23: + li r12,-144 + lvx vr23,r12,r0 +.globl _restvr_24 +_restvr_24: + li r12,-128 + lvx vr24,r12,r0 +.globl _restvr_25 +_restvr_25: + li r12,-112 + lvx vr25,r12,r0 +.globl _restvr_26 +_restvr_26: + li r12,-96 + lvx vr26,r12,r0 +.globl _restvr_27 +_restvr_27: + li r12,-80 + lvx vr27,r12,r0 +.globl _restvr_28 +_restvr_28: + li r12,-64 + lvx vr28,r12,r0 +.globl _restvr_29 +_restvr_29: + li r12,-48 + lvx vr29,r12,r0 +.globl _restvr_30 +_restvr_30: + li r12,-32 + lvx vr30,r12,r0 +.globl _restvr_31 +_restvr_31: + li r12,-16 + lvx vr31,r12,r0 + blr + +#endif /* CONFIG_ALTIVEC */ + #endif /* CONFIG_PPC64 */ #endif diff --git a/arch/x86/crypto/ghash-clmulni-intel_asm.S b/arch/x86/crypto/ghash-clmulni-intel_asm.S index 1eb7f90cb7b9..eb4d2a254b35 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_asm.S +++ b/arch/x86/crypto/ghash-clmulni-intel_asm.S @@ -24,10 +24,6 @@ .align 16 .Lbswap_mask: .octa 0x000102030405060708090a0b0c0d0e0f -.Lpoly: - .octa 0xc2000000000000000000000000000001 -.Ltwo_one: - .octa 0x00000001000000000000000000000001 #define DATA %xmm0 #define SHASH %xmm1 @@ -131,27 +127,3 @@ ENTRY(clmul_ghash_update) movups DATA, (%rdi) .Lupdate_just_ret: ret - -/* - * void clmul_ghash_setkey(be128 *shash, const u8 *key); - * - * Calculate hash_key << 1 mod poly - */ -ENTRY(clmul_ghash_setkey) - movaps .Lbswap_mask, BSWAP - movups (%rsi), %xmm0 - PSHUFB_XMM BSWAP %xmm0 - movaps %xmm0, %xmm1 - psllq $1, %xmm0 - psrlq $63, %xmm1 - movaps %xmm1, %xmm2 - pslldq $8, %xmm1 - psrldq $8, %xmm2 - por %xmm1, %xmm0 - # reduction - pshufd $0b00100100, %xmm2, %xmm1 - pcmpeqd .Ltwo_one, %xmm1 - pand .Lpoly, %xmm1 - pxor %xmm1, %xmm0 - movups %xmm0, (%rdi) - ret diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c index b4bf0a63b520..c07446d17463 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_glue.c +++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c @@ -30,8 +30,6 @@ void clmul_ghash_mul(char *dst, const be128 *shash); void clmul_ghash_update(char *dst, const char *src, unsigned int srclen, const be128 *shash); -void clmul_ghash_setkey(be128 *shash, const u8 *key); - struct ghash_async_ctx { struct cryptd_ahash *cryptd_tfm; }; @@ -58,13 +56,23 @@ static int ghash_setkey(struct crypto_shash *tfm, const u8 *key, unsigned int keylen) { struct ghash_ctx *ctx = crypto_shash_ctx(tfm); + be128 *x = (be128 *)key; + u64 a, b; if (keylen != GHASH_BLOCK_SIZE) { crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } - clmul_ghash_setkey(&ctx->shash, key); + /* perform multiplication by 'x' in GF(2^128) */ + a = be64_to_cpu(x->a); + b = be64_to_cpu(x->b); + + ctx->shash.a = (__be64)((b << 1) | (a >> 63)); + ctx->shash.b = (__be64)((a << 1) | (b >> 63)); + + if (a >> 63) + ctx->shash.b ^= cpu_to_be64(0xc2); return 0; } diff --git a/block/blk-core.c b/block/blk-core.c index 8d2f43400f05..45f15b2e7076 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2233,7 +2233,7 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) if (!req->bio) return false; - trace_block_rq_complete(req->q, req); + trace_block_rq_complete(req->q, req, nr_bytes); /* * For fs requests, rq is just carrier of independent bio's diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 23763a1ec570..3d67e21d6b7c 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4677,21 +4677,26 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) { struct ata_queued_cmd *qc = NULL; - unsigned int i; + unsigned int i, tag; /* no command while frozen */ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) return NULL; - /* the last tag is reserved for internal command. */ - for (i = 0; i < ATA_MAX_QUEUE - 1; i++) - if (!test_and_set_bit(i, &ap->qc_allocated)) { - qc = __ata_qc_from_tag(ap, i); + for (i = 0; i < ATA_MAX_QUEUE; i++) { + tag = (i + ap->last_tag + 1) % ATA_MAX_QUEUE; + + /* the last tag is reserved for internal command. */ + if (tag == ATA_TAG_INTERNAL) + continue; + + if (!test_and_set_bit(tag, &ap->qc_allocated)) { + qc = __ata_qc_from_tag(ap, tag); + qc->tag = tag; + ap->last_tag = tag; break; } - - if (qc) - qc->tag = i; + } return qc; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index b0b00d70c166..ba150c7c67ee 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3058,7 +3058,10 @@ static int raw_cmd_copyout(int cmd, void __user *param, int ret; while (ptr) { - ret = copy_to_user(param, ptr, sizeof(*ptr)); + struct floppy_raw_cmd cmd = *ptr; + cmd.next = NULL; + cmd.kernel_data = NULL; + ret = copy_to_user(param, &cmd, sizeof(cmd)); if (ret) return -EFAULT; param += sizeof(struct floppy_raw_cmd); @@ -3112,10 +3115,11 @@ static int raw_cmd_copyin(int cmd, void __user *param, return -ENOMEM; *rcmd = ptr; ret = copy_from_user(ptr, param, sizeof(*ptr)); - if (ret) - return -EFAULT; ptr->next = NULL; ptr->buffer_length = 0; + ptr->kernel_data = NULL; + if (ret) + return -EFAULT; param += sizeof(struct floppy_raw_cmd); if (ptr->cmd_count > 33) /* the command may now also take up the space @@ -3131,7 +3135,6 @@ static int raw_cmd_copyin(int cmd, void __user *param, for (i = 0; i < 16; i++) ptr->reply[i] = 0; ptr->resultcode = 0; - ptr->kernel_data = NULL; if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) { if (ptr->length <= 0) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 385c58e8405b..0f8114de0877 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -167,7 +167,8 @@ static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port) ct->regs.ack = PINCTRL_IRQSTAT(port->id) + MXS_CLR; ct->regs.mask = PINCTRL_IRQEN(port->id); - irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0); + irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, + IRQ_NOREQUEST, 0); } static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 90b9793fd5da..18167d6bc81f 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -549,6 +549,14 @@ static const struct dmi_system_id intel_no_crt[] = { DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), }, }, + { + .callback = intel_no_crt_dmi_callback, + .ident = "DELL XPS 8700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS 8700"), + }, + }, { } }; diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index eb3d138ff55a..86aac5cdb1ce 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -1442,9 +1442,9 @@ static void process_deferred_bios(struct pool *pool) */ if (ensure_next_mapping(pool)) { spin_lock_irqsave(&pool->lock, flags); + bio_list_add(&pool->deferred_bios, bio); bio_list_merge(&pool->deferred_bios, &bios); spin_unlock_irqrestore(&pool->lock, flags); - break; } diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 8febe46e1105..9f55d40ec69e 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -250,7 +250,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand) val = __raw_readl(nand->reg + REG_FMICSR); if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, REG_FMICSR); + __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); val = __raw_readl(nand->reg + REG_SMCSR); diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 9e2dfd517aa5..539835fabe61 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -59,15 +59,12 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) struct attribute_group *attr_group; struct attribute **attributes; struct sm_sysfs_attribute *vendor_attribute; + char *vendor; - int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, - SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET); - - char *vendor = kmalloc(vendor_len, GFP_KERNEL); + vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, + SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL); if (!vendor) goto error1; - memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len); - vendor[vendor_len] = 0; /* Initialize sysfs attributes */ vendor_attribute = @@ -78,7 +75,7 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) sysfs_attr_init(&vendor_attribute->dev_attr.attr); vendor_attribute->data = vendor; - vendor_attribute->len = vendor_len; + vendor_attribute->len = strlen(vendor); vendor_attribute->dev_attr.attr.name = "vendor"; vendor_attribute->dev_attr.attr.mode = S_IRUGO; vendor_attribute->dev_attr.show = sm_attr_show; diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 108118820b36..cf5af65781f5 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -4598,22 +4598,22 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, int ch = new_channel->hw_value; u16 old_band_5ghz; - u32 tmp32; + u16 tmp16; old_band_5ghz = b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ; if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) { - tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4); + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ); } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) { b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); - tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4); + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); b43_phy_mask(dev, B43_PHY_B_BBCFG, 0x3FFF); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); } b43_chantab_phy_upload(dev, e); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index 0c74d4f2eeb4..88f73503b6f9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -985,6 +985,17 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); int err = 0; static bool iqk_initialized; + unsigned long flags; + + /* As this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); rtlhal->hw_type = HARDWARE_TYPE_RTL8192CU; err = _rtl92cu_init_mac(hw); @@ -997,7 +1008,7 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; - return err; + goto exit; } rtlhal->last_hmeboxnum = 0; /* h2c */ _rtl92cu_phy_param_tab_init(hw); @@ -1034,6 +1045,8 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) _InitPABias(hw); _update_mac_setting(hw); rtl92c_dm_init(hw); +exit: + local_irq_restore(flags); return err; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index b141c35bf926..f90eb0cd7ae5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -922,7 +922,7 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 tmp_byte = 0; - + unsigned long flags; bool rtstatus = true; u8 tmp_u1b; int err = false; @@ -934,6 +934,16 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) rtlpci->being_init_adapter = true; + /* As this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); + rtlpriv->intf_ops->disable_aspm(hw); /* 1. MAC Initialize */ @@ -951,7 +961,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now... " "Please copy FW into /lib/firmware/rtlwifi\n"); - return 1; + err = 1; + goto exit; } /* After FW download, we have to reset MAC register */ @@ -964,7 +975,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) /* 3. Initialize MAC/PHY Config by MACPHY_reg.txt */ if (!rtl92s_phy_mac_config(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "MAC Config failed\n"); - return rtstatus; + err = rtstatus; + goto exit; } /* Make sure BB/RF write OK. We should prevent enter IPS. radio off. */ @@ -974,7 +986,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) /* 4. Initialize BB After MAC Config PHY_reg.txt, AGC_Tab.txt */ if (!rtl92s_phy_bb_config(hw)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "BB Config failed\n"); - return rtstatus; + err = rtstatus; + goto exit; } /* 5. Initiailze RF RAIO_A.txt RF RAIO_B.txt */ @@ -1010,7 +1023,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) if (!rtl92s_phy_rf_config(hw)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "RF Config failed\n"); - return rtstatus; + err = rtstatus; + goto exit; } /* After read predefined TXT, we must set BB/MAC/RF @@ -1084,8 +1098,9 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_ON); rtl92s_dm_init(hw); +exit: + local_irq_restore(flags); rtlpci->being_init_adapter = false; - return err; } diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index 25506c777381..9bec1717047e 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -486,6 +486,8 @@ mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc) pthru32->dataxferaddr = kioc->buf_paddr; if (kioc->data_dir & UIOC_WR) { + if (pthru32->dataxferlen > kioc->xferlen) + return -EINVAL; if (copy_from_user(kioc->buf_vaddr, kioc->user_data, pthru32->dataxferlen)) { return (-EFAULT); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index d953a57e779d..e1a6e7a86798 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -8090,7 +8090,6 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state) mpt2sas_base_free_resources(ioc); pci_save_state(pdev); - pci_disable_device(pdev); pci_set_power_state(pdev, device_state); return 0; } diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 8880adf5fc6f..6d9caab33d81 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -186,7 +186,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) return hvc_driver; } -static int __init hvc_console_setup(struct console *co, char *options) +static int hvc_console_setup(struct console *co, char *options) { if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) return -ENODEV; diff --git a/drivers/video/aty/mach64_accel.c b/drivers/video/aty/mach64_accel.c index e45833ce975b..182bd680141f 100644 --- a/drivers/video/aty/mach64_accel.c +++ b/drivers/video/aty/mach64_accel.c @@ -4,6 +4,7 @@ */ #include +#include #include #include