Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1fb06e7 authored by tbalden's avatar tbalden Committed by Razziell
Browse files

mdss mdp: kcal for mdss_mdp_v1_7



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: default avatarflar2 <asegaert@gmail.com>
Signed-off-by: default avatarAKabhishek <abhishekkaushik.star@gmail.com>

# Conflicts:
#	drivers/video/msm/mdss/Makefile
parent 2830283f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -72,3 +72,10 @@ config FB_MSM_MDSS_FRC_DEBUG
	frame rate control (FRC) debugging features to: Collect video frame
	statistics and check whether its output pattern matches expected
	cadence.

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.
+1 −0
Original line number Diff line number Diff line
@@ -71,3 +71,4 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o
obj-$(CONFIG_COMPAT) += mdss_compat_utils.o

obj-$(CONFIG_FB_MSM_MDSS) += mdss_livedisplay.o
obj-$(CONFIG_FB_MSM_MDSS_KCAL_CTRL) += mdss_mdp_kcal_ctrl.o
+730 −0
Original line number Diff line number Diff line
/*
 * 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);
+11 −0
Original line number Diff line number Diff line
@@ -671,9 +671,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");
@@ -1170,9 +1175,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)) {