OSDN Git Service

mdss mdp: kcal for mdss_mdp_v1_7
authortbalden <illespal@gmail.com>
Sat, 16 Jul 2016 21:17:25 +0000 (14:17 -0700)
committersbwml <984419930@qq.com>
Tue, 19 Nov 2019 21:25:15 +0000 (05:25 +0800)
Adding the kcal interface by @savoca and
@faux123
porting from m9 version by @flar2

Using the MDP 1_7 userspace structs to inject
configurations of kcal to the pp_cache configs.
Overriding userspace copy with memcpy when
copy_from_user fails (in case of kcal interface
used).

Adding a simple mdp ctl struct caching to
the kcal driver to grab ctl struct for config
calls.

Could be done nicer with direct interfacing
to userspace, leaves some space to make the kernel
crash if a third party app sends faulty configs,
but there's no security issue or degradation
of functionality with this memcpy patch.

@tbalden

Signed-off-by: flar2 <asegaert@gmail.com>
Change-Id: I5f992ae31c133808bbf607d6a04817f4cdba235e

drivers/video/fbdev/msm/Kconfig
drivers/video/fbdev/msm/Makefile
drivers/video/fbdev/msm/mdss_mdp_kcal_ctrl.c [new file with mode: 0644]
drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c

index 03ee89a..4ae9349 100644 (file)
@@ -122,4 +122,11 @@ config FB_MSM_MDSS_XLOG_DEBUG
        driver during fatal errors and enable some display-driver logging
        into an internal buffer (this avoids logging overhead).
 
+config FB_MSM_MDSS_KCAL_CTRL
+       depends on FB_MSM_MDSS
+       bool "MDSS color control"
+       ---help---
+         Enable sysfs for post-processing control of mdss-mdp5 display
+         controllers in MDSS.
+
 endif
index e101b87..4a42d11 100644 (file)
@@ -64,3 +64,10 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o
 
 obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o
 obj-$(CONFIG_COMPAT) += mdss_compat_utils.o
+obj-$(CONFIG_FB_MSM_MDSS_KCAL_CTRL) += mdss_mdp_kcal_ctrl.o
+
+# TODO: remove me b/62058353
+subdir-ccflags-y += \
+       -Wno-header-guard \
+       -Wno-parentheses-equality \
+       -Wno-duplicate-decl-specifier
diff --git a/drivers/video/fbdev/msm/mdss_mdp_kcal_ctrl.c b/drivers/video/fbdev/msm/mdss_mdp_kcal_ctrl.c
new file mode 100644 (file)
index 0000000..a3b2e36
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, LGE Inc. All rights reserved
+ * Copyright (c) 2014 savoca <adeddo27@gmail.com>
+ * Copyright (c) 2014 Paul Reioux <reioux@gmail.com>
+ * Copyright (c) 2016 Pal Zoltan Illes <palilles@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB)
+#include <mach/mmi_panel_notifier.h>
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+
+#include "mdss_mdp.h"
+
+#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_Table_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_Table_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
+};
+
+struct mdss_mdp_ctl *fb0_ctl = 0;
+
+static int mdss_mdp_kcal_store_fb0_ctl(void)
+{
+       int i;
+       struct mdss_mdp_ctl *ctl;
+       struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+       if (fb0_ctl) return 1;
+       if (!mdata) {
+               pr_err("%s mdata is NULL...",__func__);
+               return 0;
+       }
+
+       for (i = 0; i < mdata->nctl; i++) {
+               ctl = mdata->ctl_off + i;
+               if (!ctl) {
+                       pr_err("%s ctl is NULL...\n",__func__);
+                       return 0;
+               }
+               if (!(ctl->mfd)) {
+                       pr_err("%s MFD is NULL...\n",__func__);
+                       return 0;
+               }
+               pr_err("%s panel name %s\n",__func__,ctl->mfd->panel_info->panel_name);
+               if ( ctl->mfd->panel_info->fb_num  == 0 ) {
+                       pr_err("%s panel found...\n",__func__);
+                       fb0_ctl = ctl;
+                       return 1;
+               }
+       }
+       return 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 (mdss_mdp_ctl_is_power_on(ctl))
+                       return true;
+       }
+
+       return false;
+}
+
+static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data)
+{
+       u32 copyback = 0;
+       struct mdp_pcc_cfg_data pcc_config;
+
+       struct mdp_pcc_data_v1_7 *payload;
+
+       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;
+
+       memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data));
+
+       pcc_config.version = mdp_pcc_v1_7;
+       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;
+
+       payload = kzalloc(sizeof(struct mdp_pcc_data_v1_7),GFP_USER);
+       payload->r.r = pcc_config.r.r;
+       payload->g.g = pcc_config.g.g;
+       payload->b.b = pcc_config.b.b;
+       pcc_config.cfg_payload = payload;
+
+       if (!mdss_mdp_kcal_store_fb0_ctl()) return;
+       mdss_mdp_pcc_config(fb0_ctl->mfd, &pcc_config, &copyback);
+       kfree(payload);
+}
+
+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 mdp_pa_data_v1_7 *payload;
+
+       struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+       if (!mdss_mdp_kcal_store_fb0_ctl()) return;
+
+       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(fb0_ctl->mfd, &pa_config, &copyback);
+       } else {
+               memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data));
+               
+               pa_v2_config.version = mdp_pa_v1_7;
+               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;
+               pa_v2_config.flags = pa_v2_config.pa_v2_data.flags;
+
+               payload = kzalloc(sizeof(struct mdp_pa_data_v1_7),GFP_USER);
+               payload->mode = pa_v2_config.flags;
+               payload->global_hue_adj = lut_data->hue;
+               payload->global_sat_adj = lut_data->sat;
+               payload->global_val_adj = lut_data->val;
+               payload->global_cont_adj = lut_data->cont;
+               pa_v2_config.cfg_payload = payload;
+
+               mdss_mdp_pa_v2_config(fb0_ctl->mfd, &pa_v2_config, &copyback);
+               kfree(payload);
+       }
+}
+
+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;
+       struct mdp_igc_lut_data_v1_7 *payload;
+
+       if (!mdss_mdp_kcal_store_fb0_ctl()) return;
+
+       memset(&igc_config, 0, sizeof(struct mdp_igc_lut_data));
+
+       igc_config.version = mdp_igc_v1_7;
+       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_Table_Inverted[0];
+       igc_config.c2_data = &igc_Table_RGB[0];
+
+       payload = kzalloc(sizeof(struct mdp_igc_lut_data_v1_7),GFP_USER);
+       payload->len = IGC_LUT_ENTRIES;
+       payload->c0_c1_data = &igc_Table_Inverted[0];
+       payload->c2_data = &igc_Table_RGB[0];
+
+       igc_config.cfg_payload = payload;
+
+       mdss_mdp_igc_lut_config(fb0_ctl->mfd, &igc_config, &copyback, copy_from_kernel);
+       kfree(payload);
+}
+
+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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       r = sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b);
+       if ((r != 3) || (kcal_r < 0 || kcal_r > 256) ||
+               (kcal_g < 0 || kcal_g > 256) || (kcal_b < 0 || kcal_b > 256))
+               return -EINVAL;
+
+       lut_data->red = kcal_r;
+       lut_data->green = kcal_g;
+       lut_data->blue = kcal_b;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_pcc(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       r = kstrtoint(buf, 10, &kcal_min);
+       if ((r) || (kcal_min < 0 || kcal_min > 256))
+               return -EINVAL;
+
+       lut_data->minimum = kcal_min;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_pcc(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       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;
+
+       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;
+}
+
+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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       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;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_igc(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       r = kstrtoint(buf, 10, &kcal_sat);
+       if ((r) || ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128))
+               return -EINVAL;
+
+       lut_data->sat = kcal_sat;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_pa(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       r = kstrtoint(buf, 10, &kcal_hue);
+       if ((r) || (kcal_hue < 0 || kcal_hue > 1536))
+               return -EINVAL;
+
+       lut_data->hue = kcal_hue;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_pa(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       r = kstrtoint(buf, 10, &kcal_val);
+       if ((r) || (kcal_val < 128 || kcal_val > 383))
+               return -EINVAL;
+
+       lut_data->val = kcal_val;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_pa(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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, r;
+       struct kcal_lut_data *lut_data = dev_get_drvdata(dev);
+
+       r = kstrtoint(buf, 10, &kcal_cont);
+       if ((r) || (kcal_cont < 128 || kcal_cont > 383))
+               return -EINVAL;
+
+       lut_data->cont = kcal_cont;
+
+       if (mdss_mdp_kcal_is_panel_on())
+               mdss_mdp_kcal_update_pa(lut_data);
+       else
+               lut_data->queue_changes = true;
+
+       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 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;
+}
+
+#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 kcal_ctrl_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct kcal_lut_data *lut_data;
+
+       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;
+       }
+
+       platform_set_drvdata(pdev, lut_data);
+
+       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->val = DEF_PA;
+       lut_data->cont = DEF_PA;
+
+       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);
+       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__);
+               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 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);
+
+#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;
+}
+
+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);
index a5ec709..e8f0a07 100644 (file)
@@ -680,9 +680,14 @@ static int pp_pcc_cache_params_v1_7(struct mdp_pcc_cfg_data *config,
                        (void *) v17_cache_data;
                if (copy_from_user(&v17_usr_config, config->cfg_payload,
                                   sizeof(v17_usr_config))) {
+#ifdef CONFIG_FB_MSM_MDSS_KCAL_CTRL
+                       memcpy(&v17_usr_config, config->cfg_payload, sizeof(v17_usr_config));
+                       ret = 0;
+#else
                        pr_err("failed to copy v17 pcc\n");
                        ret = -EFAULT;
                        goto pcc_config_exit;
+#endif
                }
                if ((config->ops & MDP_PP_OPS_DISABLE)) {
                        pr_debug("disable pcc\n");
@@ -1194,9 +1199,15 @@ static int pp_pa_cache_params_v1_7(struct mdp_pa_v2_cfg_data *config,
 
        if (copy_from_user(&pa_usr_config, config->cfg_payload,
                           sizeof(pa_usr_config))) {
+#ifdef CONFIG_FB_MSM_MDSS_KCAL_CTRL
+               pr_err("%s Failed to copy v1_7 PA - copying in kernel \n",__func__);
+               memcpy(&pa_usr_config, config->cfg_payload, sizeof(pa_usr_config));
+               ret = 0;
+#else
                pr_err("Failed to copy v1_7 PA\n");
                ret = -EFAULT;
                goto pa_config_exit;
+#endif
        }
 
        if ((config->flags & MDP_PP_OPS_DISABLE)) {