blob: c3b4c5258fb63b26c17f8c37ee39af2feeb3f9f5 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/lcd.h>
#include <linux/fb.h>
#include <linux/pm_runtime.h>
#include "panel_drv.h"
#include "mdnie.h"
#include "copr.h"
#ifdef CONFIG_DISPLAY_USE_INFO
#include "dpui.h"
#endif
#ifdef MDNIE_SELF_TEST
int g_coord_x = MIN_WCRD_X;
int g_coord_y = MIN_WCRD_Y;
#endif
static const char * const mdnie_maptbl_name[] = {
[MDNIE_UI_MAPTBL] = "ui",
[MDNIE_VIDEO_MAPTBL] = "video",
[MDNIE_CAMERA_MAPTBL] = "camera",
[MDNIE_GALLERY_MAPTBL] = "gallery",
[MDNIE_VT_MAPTBL] = "vt",
[MDNIE_BROWSER_MAPTBL] = "browser",
[MDNIE_EBOOK_MAPTBL] = "ebook",
[MDNIE_EMAIL_MAPTBL] = "email",
[MDNIE_GAME_LOW_MAPTBL] = "game_low",
[MDNIE_GAME_MID_MAPTBL] = "game_mid",
[MDNIE_GAME_HIGH_MAPTBL] = "game_high",
[MDNIE_VIDEO_ENHANCER_MAPTBL] = "video_enhancer",
[MDNIE_VIDEO_ENHANCER_THIRD_MAPTBL] = "video_enhancer_third",
[MDNIE_HMD_8_MAPTBL] = "hmd_8",
[MDNIE_HMD_16_MAPTBL] = "hmd_16",
#if defined(CONFIG_TDMB)
[MDNIE_DMB_MAPTBL] = "dmb",
#endif
/* ACCESSIBILITY */
[MDNIE_NEGATIVE_MAPTBL] = "negative",
[MDNIE_COLOR_BLIND_MAPTBL] = "color_blind",
[MDNIE_SCREEN_CURTAIN_MAPTBL] = "screen_curtain",
[MDNIE_GRAYSCALE_MAPTBL] = "grayscale",
[MDNIE_GRAYSCALE_NEGATIVE_MAPTBL] = "grayscale_negative",
[MDNIE_COLOR_BLIND_HBM_MAPTBL] = "color_blind_hbm",
/* BYPASS */
[MDNIE_BYPASS_MAPTBL] = "bypass",
/* HBM */
[MDNIE_HBM_MAPTBL] = "hbm",
/* HMD */
[MDNIE_HMD_MAPTBL] = "hmd",
/* HDR */
[MDNIE_HDR_MAPTBL] = "hdr",
/* NIGHT */
[MDNIE_NIGHT_MAPTBL] = "night",
/* LIGHT_NOTIFICATION */
[MDNIE_LIGHT_NOTIFICATION_MAPTBL] = "light_notification",
/* COLOR_LENS */
[MDNIE_COLOR_LENS_MAPTBL] = "color_lens",
};
static const char * const scr_white_mode_name[] = {
[SCR_WHITE_MODE_NONE] = "none",
[SCR_WHITE_MODE_COLOR_COORDINATE] = "coordinate",
[SCR_WHITE_MODE_ADJUST_LDU] = "adjust-ldu",
[SCR_WHITE_MODE_SENSOR_RGB] = "sensor-rgb",
};
static const char * const mdnie_mode_name[] = {
[MDNIE_OFF_MODE] = "off",
[MDNIE_BYPASS_MODE] = "bypass",
[MDNIE_LIGHT_NOTIFICATION_MODE] = "light_notification",
[MDNIE_ACCESSIBILITY_MODE] = "accessibility",
[MDNIE_COLOR_LENS_MODE] = "color_lens",
[MDNIE_HDR_MODE] = "hdr",
[MDNIE_HMD_MODE] = "hmd",
[MDNIE_NIGHT_MODE] = "night",
[MDNIE_HBM_MODE] = "hbm",
[MDNIE_DMB_MODE] = "dmb",
[MDNIE_SCENARIO_MODE] = "scenario",
};
static const char * const scenario_mode_name[] = {
[DYNAMIC] = "dynamic",
[STANDARD] = "standard",
[NATURAL] = "natural",
[MOVIE] = "movie",
[AUTO] = "auto",
};
static const char * const scenario_name[] = {
[UI_MODE] = "ui",
[VIDEO_NORMAL_MODE] = "video_normal",
[CAMERA_MODE] = "camera",
[NAVI_MODE] = "navi",
[GALLERY_MODE] = "gallery",
[VT_MODE] = "vt",
[BROWSER_MODE] = "browser",
[EBOOK_MODE] = "ebook",
[EMAIL_MODE] = "email",
[GAME_LOW_MODE] = "game_low",
[GAME_MID_MODE] = "game_mid",
[GAME_HIGH_MODE] = "game_high",
[VIDEO_ENHANCER] = "video_enhancer",
[VIDEO_ENHANCER_THIRD] = "video_enhancer_third",
[HMD_8_MODE] = "hmd_8",
[HMD_16_MODE] = "hmd_16",
[DMB_NORMAL_MODE] = "dmb_normal",
};
static const char * const accessibility_name[] = {
[ACCESSIBILITY_OFF] = "off",
[NEGATIVE] = "negative",
[COLOR_BLIND] = "color_blind",
[SCREEN_CURTAIN] = "screen_curtain",
[GRAYSCALE] = "grayscale",
[GRAYSCALE_NEGATIVE] = "grayscale_negative",
[COLOR_BLIND_HBM] = "color_blind_hbm",
};
int mdnie_current_state(struct mdnie_info *mdnie)
{
struct panel_device *panel =
container_of(mdnie, struct panel_device, mdnie);
int mdnie_mode;
if (IS_BYPASS_MODE(mdnie))
mdnie_mode = MDNIE_BYPASS_MODE;
else if (IS_LIGHT_NOTIFICATION_MODE(mdnie))
mdnie_mode = MDNIE_LIGHT_NOTIFICATION_MODE;
else if (IS_ACCESSIBILITY_MODE(mdnie))
mdnie_mode = MDNIE_ACCESSIBILITY_MODE;
else if (IS_COLOR_LENS_MODE(mdnie))
mdnie_mode = MDNIE_COLOR_LENS_MODE;
else if (IS_HDR_MODE(mdnie))
mdnie_mode = MDNIE_HDR_MODE;
else if (IS_HMD_MODE(mdnie))
mdnie_mode = MDNIE_HMD_MODE;
else if (IS_NIGHT_MODE(mdnie))
mdnie_mode = MDNIE_NIGHT_MODE;
else if (IS_HBM_MODE(mdnie))
mdnie_mode = MDNIE_HBM_MODE;
#if defined(CONFIG_TDMB)
else if (IS_DMB_MODE(mdnie))
mdnie_mode = MDNIE_DMB_MODE;
#endif
else if (IS_SCENARIO_MODE(mdnie))
mdnie_mode = MDNIE_SCENARIO_MODE;
else
mdnie_mode = MDNIE_OFF_MODE;
if (panel->state.cur_state == PANEL_STATE_ALPM &&
((mdnie_mode == MDNIE_ACCESSIBILITY_MODE &&
(mdnie->props.accessibility == NEGATIVE ||
mdnie->props.accessibility == GRAYSCALE_NEGATIVE)) ||
(mdnie_mode == MDNIE_SCENARIO_MODE && !IS_LDU_MODE(mdnie)) ||
mdnie_mode == MDNIE_COLOR_LENS_MODE ||
mdnie_mode == MDNIE_DMB_MODE ||
mdnie_mode == MDNIE_HDR_MODE ||
mdnie_mode == MDNIE_LIGHT_NOTIFICATION_MODE ||
mdnie_mode == MDNIE_HMD_MODE)) {
pr_debug("%s block mdnie (%s->%s) in doze mode\n",
__func__, mdnie_mode_name[mdnie_mode],
mdnie_mode_name[MDNIE_BYPASS_MODE]);
mdnie_mode = MDNIE_BYPASS_MODE;
}
return mdnie_mode;
}
int mdnie_get_maptbl_index(struct mdnie_info *mdnie)
{
int index;
int mdnie_mode = mdnie_current_state(mdnie);
switch (mdnie_mode) {
case MDNIE_BYPASS_MODE:
index = MDNIE_BYPASS_MAPTBL;
break;
case MDNIE_LIGHT_NOTIFICATION_MODE:
index = MDNIE_LIGHT_NOTIFICATION_MAPTBL;
break;
case MDNIE_ACCESSIBILITY_MODE:
index = MAPTBL_IDX_ACCESSIBILITY(mdnie->props.accessibility);
break;
case MDNIE_COLOR_LENS_MODE:
index = MDNIE_COLOR_LENS_MAPTBL;
break;
case MDNIE_HDR_MODE:
index = MDNIE_HDR_MAPTBL;
break;
case MDNIE_HMD_MODE:
index = MDNIE_HMD_MAPTBL;
break;
case MDNIE_NIGHT_MODE:
index = MDNIE_NIGHT_MAPTBL;
break;
case MDNIE_HBM_MODE:
index = MDNIE_HBM_MAPTBL;
break;
#if defined(CONFIG_TDMB)
case MDNIE_DMB_MODE:
index = MDNIE_DMB_MAPTBL;
break;
#endif
case MDNIE_SCENARIO_MODE:
index = MAPTBL_IDX_SCENARIO(mdnie->props.scenario);
break;
default:
index = -EINVAL;
pr_err("%s, unknown mdnie\n", __func__);
break;
}
if (index >= 0 && index < MAX_MDNIE_MAPTBL) {
#ifdef DEBUG_PANEL
panel_dbg("%s, mdnie %s(%d), maptbl %s(%d) found\n",
__func__, mdnie_mode_name[mdnie_mode],
mdnie_mode, mdnie_maptbl_name[index], index);
#endif
} else {
panel_err("%s, mdnie %s(%d), maptbl not found!! (%d)\n",
__func__, mdnie_mode_name[mdnie_mode],
mdnie_mode, index);
index = MDNIE_UI_MAPTBL;
}
return index;
}
struct maptbl *mdnie_find_maptbl(struct mdnie_info *mdnie)
{
int index = mdnie_get_maptbl_index(mdnie);
if (unlikely(index < 0 || index >= MAX_MDNIE_MAPTBL)) {
pr_err("%s, failed to find maptbl %d\n", __func__, index);
return NULL;
}
return &mdnie->maptbl[index];
}
struct maptbl *mdnie_find_etc_maptbl(struct mdnie_info *mdnie, int index)
{
if (unlikely(index < 0 || index >= mdnie->nr_etc_maptbl)) {
pr_err("%s, failed to find maptbl %d\n", __func__, index);
return NULL;
}
return &mdnie->etc_maptbl[index];
}
struct maptbl *mdnie_find_scr_white_maptbl(struct mdnie_info *mdnie, int index)
{
if (unlikely(index < 0 || index >= mdnie->nr_scr_white_maptbl)) {
pr_err("%s, failed to find maptbl %d\n", __func__, index);
return NULL;
}
return &mdnie->scr_white_maptbl[index];
}
static int mdnie_get_coordinate(struct mdnie_info *mdnie, int *x, int *y)
{
struct panel_device *panel =
container_of(mdnie, struct panel_device, mdnie);
struct panel_info *panel_data = &panel->panel_data;
u8 coordinate[PANEL_COORD_LEN] = { 0, };
int ret;
if (!mdnie || !x || !y) {
pr_err("%s, invalid parameter\n", __func__);
return -EINVAL;
}
ret = resource_copy_by_name(panel_data, coordinate, "coordinate");
if (ret < 0) {
pr_err("%s, failed to copy 'coordinate' resource\n", __func__);
return -EINVAL;
}
*x = (coordinate[0] << 8) | coordinate[1];
*y = (coordinate[2] << 8) | coordinate[3];
#ifdef MDNIE_SELF_TEST
*x = g_coord_x;
*y = g_coord_y;
#endif
if (*x < MIN_WCRD_X || *x > MAX_WCRD_X ||
*y < MIN_WCRD_Y || *y > MAX_WCRD_Y)
pr_warn("%s, need to check coord_x:%d coord_y:%d)\n", __func__, *x, *y);
return 0;
}
static bool mdnie_coordinate_changed(struct mdnie_info *mdnie)
{
int x, y;
mdnie_get_coordinate(mdnie, &x, &y);
return (mdnie->props.wcrd_x != x || mdnie->props.wcrd_y != y);
}
static int mdnie_coordinate_area(struct mdnie_info *mdnie, int x, int y)
{
s64 result[MAX_CAL_LINE];
int i, area;
for (i = 0; i < MAX_CAL_LINE; i++)
result[i] = COLOR_OFFSET_FUNC(x, y,
mdnie->props.line[i].num,
mdnie->props.line[i].den,
mdnie->props.line[i].con);
area = RECOGNIZE_REGION(result[H_LINE], result[V_LINE]);
pr_debug("%s, coord %d %d area Q%d, result %lld %lld\n",
__func__, x, y, area + 1, result[H_LINE], result[V_LINE]);
return area;
}
static int mdnie_coordinate_tune_x(struct mdnie_info *mdnie, int x, int y)
{
int res, area;
area = mdnie_coordinate_area(mdnie, x, y);
res = ((mdnie->props.coef[area].a * x) + (mdnie->props.coef[area].b * y) +
(((mdnie->props.coef[area].c * x + (1L << 9)) >> 10) * y) +
(mdnie->props.coef[area].d * 10000) + (1L << 9)) >> 10;
return max(min(res, 1024), 0);
}
static int mdnie_coordinate_tune_y(struct mdnie_info *mdnie, int x, int y)
{
int res, area;
area = mdnie_coordinate_area(mdnie, x, y);
res = ((mdnie->props.coef[area].e * x) + (mdnie->props.coef[area].f * y) +
(((mdnie->props.coef[area].g * x + (1L << 9)) >> 10) * y) +
(mdnie->props.coef[area].h * 10000) + (1L << 9)) >> 10;
return max(min(res, 1024), 0);
}
static void mdnie_coordinate_tune_rgb(struct mdnie_info *mdnie, int x, int y, u8 (*tune_rgb)[MAX_COLOR])
{
static int pt[MAX_QUAD][MAX_RGB_PT] = {
{ CCRD_PT_5, CCRD_PT_2, CCRD_PT_6, CCRD_PT_3 }, /* QUAD_1 */
{ CCRD_PT_5, CCRD_PT_2, CCRD_PT_4, CCRD_PT_1 }, /* QUAD_2 */
{ CCRD_PT_5, CCRD_PT_8, CCRD_PT_4, CCRD_PT_7 }, /* QUAD_3 */
{ CCRD_PT_5, CCRD_PT_8, CCRD_PT_6, CCRD_PT_9 }, /* QUAD_4 */
};
int i, c, type, area, tune_x, tune_y, res[MAX_COLOR][MAX_RGB_PT];
s64 result[MAX_CAL_LINE];
area = mdnie_coordinate_area(mdnie, x, y);
if (((x - mdnie->props.cal_x_center) * (x - mdnie->props.cal_x_center) + (y - mdnie->props.cal_y_center) * (y - mdnie->props.cal_y_center)) <= mdnie->props.cal_boundary_center) {
tune_x = 0;
tune_y = 0;
} else {
tune_x = mdnie_coordinate_tune_x(mdnie, x, y);
tune_y = mdnie_coordinate_tune_y(mdnie, x, y);
}
for (i = 0; i < MAX_CAL_LINE; i++)
result[i] = COLOR_OFFSET_FUNC(x, y,
mdnie->props.line[i].num,
mdnie->props.line[i].den,
mdnie->props.line[i].con);
for (type = 0; type < MAX_WCRD_TYPE; type++) {
for (c = 0; c < MAX_COLOR; c++) {
for (i = 0; i < MAX_RGB_PT; i++)
res[c][i] = mdnie->props.vtx[type][pt[area][i]][c];
tune_rgb[type][c] =
((((res[c][RGB_00] * (1024 - tune_x) + (res[c][RGB_10] * tune_x)) * (1024 - tune_y)) +
((res[c][RGB_01] * (1024 - tune_x) + (res[c][RGB_11] * tune_x)) * tune_y)) + (1L << 19)) >> 20;
}
}
panel_info("coord (x:%4d y:%4d Q%d compV:%8lld compH:%8lld)\t"
"tune_coord (%4d %4d) tune_rgb[ADT] (%3d %3d %3d) tune_rgb[D65] (%3d %3d %3d)\n",
x, y, area + 1, result[H_LINE], result[V_LINE], tune_x, tune_y,
tune_rgb[WCRD_TYPE_ADAPTIVE][0], tune_rgb[WCRD_TYPE_ADAPTIVE][1],
tune_rgb[WCRD_TYPE_ADAPTIVE][2], tune_rgb[WCRD_TYPE_D65][0],
tune_rgb[WCRD_TYPE_D65][1], tune_rgb[WCRD_TYPE_D65][2]);
}
static void mdnie_coordinate_tune(struct mdnie_info *mdnie)
{
int x, y;
mdnie_get_coordinate(mdnie, &x, &y);
mdnie->props.wcrd_x = x;
mdnie->props.wcrd_y = y;
mdnie_coordinate_tune_rgb(mdnie, x, y, mdnie->props.coord_wrgb);
}
static int panel_set_mdnie(struct panel_device *panel)
{
int ret;
struct mdnie_info *mdnie = &panel->mdnie;
int mdnie_mode = mdnie_current_state(mdnie);
if (panel == NULL) {
panel_err("PANEL:ERR:%s:panel is null\n", __func__);
return -EINVAL;
}
if (!IS_PANEL_ACTIVE(panel))
return 0;
#ifdef CONFIG_SUPPORT_AFC
pr_info("%s, do mdnie-seq (mode:%s, afc:%s)\n",
__func__, mdnie_mode_name[mdnie_mode],
!mdnie->props.afc_on ? "off" : "on");
#else
pr_info("%s, do mdnie-seq (mode:%s)\n",
__func__, mdnie_mode_name[mdnie_mode]);
#endif
ret = 0;
mutex_lock(&panel->op_lock);
ret = panel_do_seqtbl(panel, &mdnie->seqtbl[MDNIE_SET_SEQ]);
if (unlikely(ret < 0))
pr_err("%s, failed to write seqtbl\n", __func__);
#ifdef CONFIG_SUPPORT_AFC
ret = panel_do_seqtbl(panel, !mdnie->props.afc_on ?
&mdnie->seqtbl[MDNIE_AFC_OFF_SEQ] :
&mdnie->seqtbl[MDNIE_AFC_ON_SEQ]);
if (unlikely(ret < 0))
pr_err("%s, failed to write seqtbl\n", __func__);
#endif
mutex_unlock(&panel->op_lock);
return ret;
}
static void mdnie_maptbl_init(struct mdnie_info *mdnie, int index)
{
int i;
if (((index + 1) * mdnie->nr_reg) > mdnie->nr_maptbl) {
pr_err("%s out of range index %d\n", __func__, index);
return;
}
for (i = 0; i < mdnie->nr_reg; i++)
maptbl_init(&mdnie->maptbl[index * mdnie->nr_reg + i]);
}
static void scr_white_maptbl_init(struct mdnie_info *mdnie, int index)
{
if ((index + 1) > mdnie->nr_scr_white_maptbl) {
pr_err("%s out of range index %d\n", __func__, index);
return;
}
maptbl_init(&mdnie->scr_white_maptbl[index]);
}
static void scr_white_maptbls_init(struct mdnie_info *mdnie)
{
int i;
for (i = 0; i < mdnie->nr_scr_white_maptbl; i++)
maptbl_init(&mdnie->scr_white_maptbl[i]);
}
static void mdnie_update_scr_white_mode(struct mdnie_info *mdnie)
{
int mdnie_mode = mdnie_current_state(mdnie);
if (mdnie_mode == MDNIE_SCENARIO_MODE) {
if ((IS_LDU_MODE(mdnie)) && (mdnie->props.scenario != EBOOK_MODE)) {
mdnie->props.scr_white_mode = SCR_WHITE_MODE_ADJUST_LDU;
} else if (mdnie->props.update_sensorRGB &&
mdnie->props.mode == AUTO &&
(mdnie->props.scenario == BROWSER_MODE ||
mdnie->props.scenario == EBOOK_MODE)) {
mdnie->props.scr_white_mode = SCR_WHITE_MODE_SENSOR_RGB;
mdnie->props.update_sensorRGB = false;
} else if (mdnie->props.scenario <= SCENARIO_MAX &&
mdnie->props.scenario != EBOOK_MODE) {
mdnie->props.scr_white_mode =
SCR_WHITE_MODE_COLOR_COORDINATE;
} else {
mdnie->props.scr_white_mode = SCR_WHITE_MODE_NONE;
}
} else if (mdnie_mode == MDNIE_HBM_MODE) {
mdnie->props.scr_white_mode =
SCR_WHITE_MODE_COLOR_COORDINATE;
} else {
mdnie->props.scr_white_mode = SCR_WHITE_MODE_NONE;
}
#ifdef DEBUG_PANEL
panel_info("%s scr_white_mode %s\n", __func__,
scr_white_mode_name[mdnie->props.scr_white_mode]);
#endif
}
int panel_mdnie_update(struct panel_device *panel)
{
int ret;
struct mdnie_info *mdnie = &panel->mdnie;
mutex_lock(&mdnie->lock);
if (!IS_MDNIE_ENABLED(mdnie)) {
dev_err(mdnie->dev, "mdnie is off state\n");
mutex_unlock(&mdnie->lock);
return -EINVAL;
}
if (mdnie_coordinate_changed(mdnie)) {
mdnie_coordinate_tune(mdnie);
scr_white_maptbls_init(mdnie);
}
mdnie_update_scr_white_mode(mdnie);
ret = panel_set_mdnie(panel);
if (ret < 0) {
pr_err("%s, failed to set mdnie %d\n", __func__, ret);
mutex_unlock(&mdnie->lock);
return ret;
}
mutex_unlock(&mdnie->lock);
#ifdef CONFIG_EXYNOS_DECON_LCD_COPR
copr_update_start(&panel->copr, 3);
#endif
return 0;
}
static int mdnie_update(struct mdnie_info *mdnie)
{
int ret;
struct panel_device *panel =
container_of(mdnie, struct panel_device, mdnie);
panel_wake_lock(panel);
ret = panel_mdnie_update(panel);
panel_wake_unlock(panel);
return ret;
}
#ifdef MDNIE_SELF_TEST
static void mdnie_coordinate_tune_test(struct mdnie_info *mdnie)
{
int i, x, y;
u8 tune_rgb[MAX_WCRD_TYPE][MAX_COLOR];
int input[27][2] = {
{ 2936, 3144 }, { 2987, 3203 }, { 3032, 3265 }, { 2954, 3108 }, { 2991, 3158 }, { 3041, 3210 }, { 2967, 3058 }, { 3004, 3091 },
{ 3063, 3156 }, { 2985, 3206 }, { 3032, 3238 }, { 2955, 3094 }, { 2997, 3151 }, { 3045, 3191 }, { 2971, 3047 }, { 3020, 3100 },
{ 3066, 3144 }, { 2983, 3131 }, { 2987, 3170 }, { 2975, 3159 }, { 3021, 3112 }, { 2982, 3122 }, { 2987, 3130 }, { 2930, 3260 },
{ 2930, 3050 }, { 3060, 3050 }, { 3060, 3260 },
};
u8 output[27][MAX_COLOR] = {
{ 0xFF, 0xFA, 0xF9 }, { 0xFE, 0xFA, 0xFE }, { 0xF8, 0xF6, 0xFF }, { 0xFF, 0xFD, 0xFB },
{ 0xFF, 0xFE, 0xFF }, { 0xF9, 0xFA, 0xFF }, { 0xFC, 0xFF, 0xF9 }, { 0xFB, 0xFF, 0xFB },
{ 0xF9, 0xFF, 0xFF }, { 0xFE, 0xFA, 0xFE }, { 0xF9, 0xF8, 0xFF }, { 0xFE, 0xFE, 0xFA },
{ 0xFE, 0xFF, 0xFF }, { 0xF8, 0xFC, 0xFF }, { 0xFA, 0xFF, 0xF8 }, { 0xF9, 0xFF, 0xFC },
{ 0xF8, 0xFF, 0xFF }, { 0xFE, 0xFF, 0xFD }, { 0xFF, 0xFD, 0xFF }, { 0xFF, 0xFD, 0xFD },
{ 0xF9, 0xFF, 0xFC }, { 0xFE, 0xFF, 0xFD }, { 0xFE, 0xFF, 0xFD }, { 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF }, { 0xFF, 0xFF, 0xFF }, { 0xFF, 0xFF, 0xFF },
};
for (x = MIN_WCRD_X; x <= MAX_WCRD_X; x += 10)
for (y = MIN_WCRD_Y; y <= MAX_WCRD_Y; y += 10)
mdnie_coordinate_tune_rgb(mdnie, x, y, tune_rgb);
for (i = 0; i < 27; i++) {
g_coord_x = input[i][0];
g_coord_y = input[i][1];
mdnie_update(mdnie);
panel_info("compare %02X %02X %02X : %02X %02X %02X (%s)\n",
output[i][0], output[i][1], output[i][2],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][0],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][1],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][2],
output[i][0] == mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][0] &&
output[i][1] == mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][1] &&
output[i][2] == mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][2] ? "SUCCESS" : "FAILED");
}
}
#endif
static ssize_t mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.mode);
}
static ssize_t mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int value = 0;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (value >= MODE_MAX) {
dev_err(dev, "%s: invalid value=%d\n", __func__, value);
return -EINVAL;
}
dev_info(dev, "%s: value=%d\n", __func__, value);
mutex_lock(&mdnie->lock);
mdnie->props.mode = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t scenario_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.scenario);
}
static ssize_t scenario_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (!SCENARIO_IS_VALID(value)) {
dev_err(dev, "%s: invalid scenario %d\n", __func__, value);
return -EINVAL;
}
dev_info(dev, "%s: value=%d\n", __func__, value);
mutex_lock(&mdnie->lock);
mdnie->props.scenario = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t accessibility_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.accessibility);
}
static ssize_t accessibility_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int s[12] = {0, };
int i, value = 0, ret;
ret = sscanf(buf, "%d %x %x %x %x %x %x %x %x %x %x %x %x",
&value, &s[0], &s[1], &s[2], &s[3],
&s[4], &s[5], &s[6], &s[7], &s[8], &s[9], &s[10], &s[11]);
if (ret <= 0 || ret > ARRAY_SIZE(s) + 1 ||
((ret - 1) > (MAX_MDNIE_SCR_LEN / 2))) {
dev_err(dev, "%s: invalid size %d\n", __func__, ret);
return -EINVAL;
}
if (value < 0 || value >= ACCESSIBILITY_MAX) {
dev_err(dev, "%s: unknown accessibility %d\n", __func__, value);
return -EINVAL;
}
dev_info(dev, "%s: value: %d, cnt: %d\n", __func__, value, ret);
mutex_lock(&mdnie->lock);
mdnie->props.accessibility = value;
if (ret > 1 && (value == COLOR_BLIND || value == COLOR_BLIND_HBM)) {
for (i = 0; i < ret - 1; i++) {
mdnie->props.scr[i * 2 + 0] = GET_LSB_8BIT(s[i]);
mdnie->props.scr[i * 2 + 1] = GET_MSB_8BIT(s[i]);
}
mdnie->props.sz_scr = (ret - 1) * 2;
mdnie_maptbl_init(mdnie,
MAPTBL_IDX_ACCESSIBILITY(mdnie->props.accessibility));
}
mutex_unlock(&mdnie->lock);
dev_info(dev, "%s: %s\n", __func__, buf);
mdnie_update(mdnie);
return count;
}
static ssize_t bypass_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.bypass);
}
static ssize_t bypass_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (value >= BYPASS_MAX) {
dev_err(dev, "%s: invalid value=%d\n", __func__, value);
return -EINVAL;
}
dev_info(dev, "%s: value=%d\n", __func__, value);
value = (value) ? BYPASS_ON : BYPASS_OFF;
mutex_lock(&mdnie->lock);
mdnie->props.bypass = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t lux_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.hbm);
}
static ssize_t lux_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int ret, value;
ret = kstrtoint(buf, 0, &value);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, value);
mutex_lock(&mdnie->lock);
/* TODO : IS HBM ON CONDITION ALWAYS 10000 LUX ? */
mdnie->props.hbm = (value < 40000) ? 0 : 1;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
/* Temporary solution: Do not use this sysfs as official purpose */
static ssize_t mdnie_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int maptbl_index = mdnie_get_maptbl_index(mdnie);
int mdnie_mode = mdnie_current_state(mdnie);
unsigned int i, len = 0;
if (!IS_MDNIE_ENABLED(mdnie)) {
dev_err(mdnie->dev, "mdnie state is off\n");
return -EINVAL;
}
len += snprintf(buf + len, PAGE_SIZE - len,
"mdnie %s-mode, maptbl %s(%d)\n",
mdnie_mode_name[mdnie_mode], (maptbl_index < 0) ?
"invalid" : mdnie_maptbl_name[maptbl_index], maptbl_index);
len += snprintf(buf + len, PAGE_SIZE - len,
"accessibility %s(%d), hdr %d, hmd %d, hbm %d\n",
accessibility_name[mdnie->props.accessibility],
mdnie->props.accessibility, mdnie->props.hdr,
mdnie->props.hmd, mdnie->props.hbm);
len += snprintf(buf + len, PAGE_SIZE - len,
"scenario %s(%d), mode %s(%d)\n",
scenario_name[mdnie->props.scenario], mdnie->props.scenario,
scenario_mode_name[mdnie->props.mode], mdnie->props.mode);
len += snprintf(buf + len, PAGE_SIZE - len, "scr_white_mode %s\n",
scr_white_mode_name[mdnie->props.scr_white_mode]);
len += snprintf(buf + len, PAGE_SIZE - len,
"mdnie_ldu %d, coord x %d, y %d area Q%d\n",
mdnie->props.ldu, mdnie->props.wcrd_x, mdnie->props.wcrd_y,
mdnie_coordinate_area(mdnie, mdnie->props.wcrd_x, mdnie->props.wcrd_y) + 1);
len += snprintf(buf + len, PAGE_SIZE - len,
"coord_wrgb[adpt] r:%d(%02X) g:%d(%02X) b:%d(%02X)\n",
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][0],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][0],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][1],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][1],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][2],
mdnie->props.coord_wrgb[WCRD_TYPE_ADAPTIVE][2]);
len += snprintf(buf + len, PAGE_SIZE - len,
"coord_wrgb[d65] r:%d(%02X) g:%d(%02X) b:%d(%02X)\n",
mdnie->props.coord_wrgb[WCRD_TYPE_D65][0],
mdnie->props.coord_wrgb[WCRD_TYPE_D65][0],
mdnie->props.coord_wrgb[WCRD_TYPE_D65][1],
mdnie->props.coord_wrgb[WCRD_TYPE_D65][1],
mdnie->props.coord_wrgb[WCRD_TYPE_D65][2],
mdnie->props.coord_wrgb[WCRD_TYPE_D65][2]);
len += snprintf(buf + len, PAGE_SIZE - len, "cur_wrgb r:%d(%02X) g:%d(%02X) b:%d(%02X)\n",
mdnie->props.cur_wrgb[0], mdnie->props.cur_wrgb[0],
mdnie->props.cur_wrgb[1], mdnie->props.cur_wrgb[1],
mdnie->props.cur_wrgb[2], mdnie->props.cur_wrgb[2]);
len += snprintf(buf + len, PAGE_SIZE - len,
"ssr_wrgb r:%d(%02X) g:%d(%02X) b:%d(%02X)\n",
mdnie->props.ssr_wrgb[0], mdnie->props.ssr_wrgb[0],
mdnie->props.ssr_wrgb[1], mdnie->props.ssr_wrgb[1],
mdnie->props.ssr_wrgb[2], mdnie->props.ssr_wrgb[2]);
len += snprintf(buf + len, PAGE_SIZE - len,
"def_wrgb r:%d(%02X) g:%d(%02X) b:%d(%02X) offset r:%d g:%d b:%d\n",
mdnie->props.def_wrgb[0], mdnie->props.def_wrgb[0],
mdnie->props.def_wrgb[1], mdnie->props.def_wrgb[1],
mdnie->props.def_wrgb[2], mdnie->props.def_wrgb[2],
mdnie->props.def_wrgb_ofs[0], mdnie->props.def_wrgb_ofs[1],
mdnie->props.def_wrgb_ofs[2]);
len += snprintf(buf + len, PAGE_SIZE - len, "scr : ");
if (mdnie->props.sz_scr) {
for (i = 0; i < mdnie->props.sz_scr; i++)
len += snprintf(buf + len, PAGE_SIZE - len,
"%02x ", mdnie->props.scr[i]);
} else {
len += snprintf(buf + len, PAGE_SIZE - len, "none");
}
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
len += snprintf(buf + len, PAGE_SIZE - len,
"night_mode %s, level %d\n",
mdnie->props.night ? "on" : "off",
mdnie->props.night_level);
#ifdef MDNIE_SELF_TEST
mdnie_coordinate_tune_test(mdnie);
#endif
return len;
}
static ssize_t sensorRGB_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d %d %d\n", mdnie->props.cur_wrgb[0],
mdnie->props.cur_wrgb[1], mdnie->props.cur_wrgb[2]);
}
static ssize_t sensorRGB_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int white_red, white_green, white_blue;
int mdnie_mode = mdnie_current_state(mdnie), ret;
ret = sscanf(buf, "%d %d %d",
&white_red, &white_green, &white_blue);
if (ret != 3)
return -EINVAL;
dev_info(dev, "%s: white_r %d, white_g %d, white_b %d\n",
__func__, white_red, white_green, white_blue);
if (mdnie_mode == MDNIE_SCENARIO_MODE &&
mdnie->props.mode == AUTO &&
(mdnie->props.scenario == BROWSER_MODE ||
mdnie->props.scenario == EBOOK_MODE)) {
mutex_lock(&mdnie->lock);
mdnie->props.ssr_wrgb[0] = white_red;
mdnie->props.ssr_wrgb[1] = white_green;
mdnie->props.ssr_wrgb[2] = white_blue;
mdnie->props.update_sensorRGB = true;
scr_white_maptbl_init(mdnie, MDNIE_SENSOR_RGB_MAPTBL);
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
}
return count;
}
static ssize_t whiteRGB_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d %d %d\n", mdnie->props.def_wrgb_ofs[0],
mdnie->props.def_wrgb_ofs[1], mdnie->props.def_wrgb_ofs[2]);
}
static ssize_t whiteRGB_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int wr_offset, wg_offset, wb_offset;
int ret;
ret = sscanf(buf, "%d %d %d",
&wr_offset, &wg_offset, &wb_offset);
if (ret != 3)
return -EINVAL;
if (!IS_VALID_WRGB_OFS(wr_offset) ||
!IS_VALID_WRGB_OFS(wg_offset) ||
!IS_VALID_WRGB_OFS(wb_offset)) {
dev_err(dev, "%s: invalid offset %d %d %d\n",
__func__, wr_offset, wg_offset, wb_offset);
return -EINVAL;
}
dev_info(dev, "%s: wr_offset %d, wg_offset %d, wb_offset %d\n",
__func__, wr_offset, wg_offset, wb_offset);
mutex_lock(&mdnie->lock);
mdnie->props.def_wrgb_ofs[0] = wr_offset;
mdnie->props.def_wrgb_ofs[1] = wg_offset;
mdnie->props.def_wrgb_ofs[2] = wb_offset;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t night_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d %d\n",
mdnie->props.night, mdnie->props.night_level);
}
static ssize_t night_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int enable, level, ret;
ret = sscanf(buf, "%d %d", &enable, &level);
if (ret != 2)
return -EINVAL;
if (level < 0 || level >= mdnie->props.num_night_level)
return -EINVAL;
dev_info(dev, "%s: night_mode %s level %d\n",
__func__, enable ? "on" : "off", level);
mutex_lock(&mdnie->lock);
mdnie->props.night = !!enable;
mdnie->props.night_level = level;
if (enable) {
/* MDNIE_NIGHT_MAPTBL update using MDNIE_ETC_NIGHT_MAPTBL */
mdnie_maptbl_init(mdnie, MDNIE_NIGHT_MAPTBL);
}
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t color_lens_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d %d %d\n",
mdnie->props.color_lens,
mdnie->props.color_lens_color,
mdnie->props.color_lens_level);
}
static ssize_t color_lens_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int enable, level, color, ret;
ret = sscanf(buf, "%d %d %d", &enable, &color, &level);
if (ret != 3)
return -EINVAL;
if (color < 0 || color >= COLOR_LENS_COLOR_MAX)
return -EINVAL;
if (level < 0 || level >= COLOR_LENS_LEVEL_MAX)
return -EINVAL;
dev_info(dev, "%s: color_lens_mode %s color %d level %d\n",
__func__, enable ? "on" : "off", color, level);
mutex_lock(&mdnie->lock);
mdnie->props.color_lens = !!enable;
mdnie->props.color_lens_color = color;
mdnie->props.color_lens_level = level;
if (enable) {
/* MDNIE_COLOR_LENS_MAPTBL update using MDNIE_ETC_COLOR_LENS_MAPTBL */
mdnie_maptbl_init(mdnie, MDNIE_COLOR_LENS_MAPTBL);
}
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t hdr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.hdr);
}
static ssize_t hdr_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (value >= HDR_MAX) {
dev_err(dev, "%s: invalid value=%d\n", __func__, value);
return -EINVAL;
}
dev_info(dev, "%s: value=%d\n", __func__, value);
mutex_lock(&mdnie->lock);
mdnie->props.hdr = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t light_notification_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", mdnie->props.light_notification);
}
static ssize_t light_notification_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (value >= LIGHT_NOTIFICATION_MAX)
return -EINVAL;
dev_info(dev, "%s: value=%d\n", __func__, value);
mutex_lock(&mdnie->lock);
mdnie->props.light_notification = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t mdnie_ldu_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d %d %d\n", mdnie->props.cur_wrgb[0],
mdnie->props.cur_wrgb[1], mdnie->props.cur_wrgb[2]);
}
static ssize_t mdnie_ldu_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int value, ret;
ret = kstrtoint(buf, 10, &value);
if (ret < 0)
return ret;
if (value < 0 || value >= MAX_LDU_MODE) {
dev_err(dev, "%s: out of range %d\n",
__func__, value);
return -EINVAL;
}
dev_info(dev, "%s: value=%d\n", __func__, value);
mutex_lock(&mdnie->lock);
mdnie->props.ldu = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
#ifdef CONFIG_SUPPORT_HMD
static ssize_t hmt_color_temperature_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "hmd_mode: %d\n", mdnie->props.hmd);
}
static ssize_t hmt_color_temperature_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (value >= HMD_MDNIE_MAX)
return -EINVAL;
if (value == mdnie->props.hmd)
return count;
dev_info(dev, "%s: value=%d\n", __func__, value);
mutex_lock(&mdnie->lock);
mdnie->props.hmd = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
#endif
#ifdef CONFIG_SUPPORT_AFC
static ssize_t afc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int len = 0;
size_t i;
len += snprintf(buf + len, PAGE_SIZE - len,
"%d", mdnie->props.afc_on);
for (i = 0; i < ARRAY_SIZE(mdnie->props.afc_roi); i++)
len += snprintf(buf + len, PAGE_SIZE - len,
" %d", mdnie->props.afc_roi[i]);
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
static ssize_t afc_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int s[12] = {0, };
int value = 0, ret;
size_t i;
ret = sscanf(buf, "%i %i %i %i %i %i %i %i %i %i %i %i %i",
&value, &s[0], &s[1], &s[2], &s[3],
&s[4], &s[5], &s[6], &s[7], &s[8], &s[9], &s[10], &s[11]);
if ((ret != 1 && ret != ARRAY_SIZE(s) + 1) ||
ARRAY_SIZE(s) != MAX_AFC_ROI_LEN) {
dev_err(dev, "%s: invalid size %d\n", __func__, ret);
return -EINVAL;
}
dev_info(dev, "%s: value=%d, cnt=%d\n", __func__, value, ret);
mutex_lock(&mdnie->lock);
mdnie->props.afc_on = !!value;
for (i = 0; i < ARRAY_SIZE(mdnie->props.afc_roi); i++)
mdnie->props.afc_roi[i] = s[i] & 0xFF;
mutex_unlock(&mdnie->lock);
dev_info(dev, "%s: %s\n", __func__, buf);
mdnie_update(mdnie);
return count;
}
#endif
struct device_attribute mdnie_dev_attrs[] = {
__PANEL_ATTR_RW(mode, 0664),
__PANEL_ATTR_RW(scenario, 0664),
__PANEL_ATTR_RW(accessibility, 0664),
__PANEL_ATTR_RW(bypass, 0664),
__PANEL_ATTR_RW(lux, 0000),
__PANEL_ATTR_RO(mdnie, 0444),
__PANEL_ATTR_RW(sensorRGB, 0664),
__PANEL_ATTR_RW(whiteRGB, 0664),
__PANEL_ATTR_RW(night_mode, 0664),
__PANEL_ATTR_RW(color_lens, 0664),
__PANEL_ATTR_RW(hdr, 0664),
__PANEL_ATTR_RW(light_notification, 0664),
__PANEL_ATTR_RW(mdnie_ldu, 0664),
#ifdef CONFIG_SUPPORT_HMD
__PANEL_ATTR_RW(hmt_color_temperature, 0664),
#endif
#ifdef CONFIG_SUPPORT_AFC
__PANEL_ATTR_RW(afc, 0664),
#endif
};
int mdnie_enable(struct mdnie_info *mdnie)
{
struct panel_device *panel =
container_of(mdnie, struct panel_device, mdnie);
if (IS_MDNIE_ENABLED(mdnie)) {
dev_info(mdnie->dev, "mdnie already enabled\n");
return 0;
}
mutex_lock(&mdnie->lock);
mdnie->props.enable = 1;
mdnie->props.light_notification = LIGHT_NOTIFICATION_OFF;
if (IS_HBM_MODE(mdnie))
mdnie->props.trans_mode = TRANS_ON;
mutex_unlock(&mdnie->lock);
panel_mdnie_update(panel);
mutex_lock(&mdnie->lock);
mdnie->props.trans_mode = TRANS_ON;
mutex_unlock(&mdnie->lock);
dev_info(mdnie->dev, "%s: done\n", __func__);
return 0;
}
int mdnie_disable(struct mdnie_info *mdnie)
{
if (!IS_MDNIE_ENABLED(mdnie)) {
dev_info(mdnie->dev, "mdnie already disabled\n");
return 0;
}
mutex_lock(&mdnie->lock);
mdnie->props.enable = 0;
mdnie->props.trans_mode = TRANS_OFF;
mdnie->props.update_sensorRGB = false;
mutex_unlock(&mdnie->lock);
dev_info(mdnie->dev, "%s: done\n", __func__);
return 0;
}
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct mdnie_info *mdnie;
struct fb_event *evdata = data;
int fb_blank;
switch (event) {
case FB_EVENT_BLANK:
break;
default:
return 0;
}
mdnie = container_of(self, struct mdnie_info, fb_notif);
fb_blank = *(int *)evdata->data;
pr_debug("%s: %d\n", __func__, fb_blank);
if (evdata->info->node != 0)
return 0;
if (fb_blank == FB_BLANK_UNBLANK) {
mutex_lock(&mdnie->lock);
mdnie->props.light_notification = LIGHT_NOTIFICATION_OFF;
if (IS_HBM_MODE(mdnie))
mdnie->props.trans_mode = TRANS_ON;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
}
return 0;
}
static int mdnie_register_fb(struct mdnie_info *mdnie)
{
memset(&mdnie->fb_notif, 0, sizeof(mdnie->fb_notif));
mdnie->fb_notif.notifier_call = fb_notifier_callback;
return fb_register_client(&mdnie->fb_notif);
}
#ifdef CONFIG_DISPLAY_USE_INFO
#define MDNIE_WOFS_ORG_PATH ("/efs/FactoryApp/mdnie")
static int mdnie_get_efs(char *filename, int *value)
{
mm_segment_t old_fs;
struct file *filp = NULL;
int fsize = 0, nread, rc, ret = 0;
u8 buf[128];
if (!filename || !value) {
pr_err("%s invalid parameter\n", __func__);
return -EINVAL;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open(filename, O_RDONLY, 0440);
if (IS_ERR(filp)) {
ret = PTR_ERR(filp);
if (ret == -ENOENT)
pr_err("%s file(%s) not exist\n", __func__, filename);
else
pr_info("%s file(%s) open error(ret %d)\n",
__func__, filename, ret);
set_fs(old_fs);
return -ENOENT;
}
if (filp->f_path.dentry && filp->f_path.dentry->d_inode)
fsize = filp->f_path.dentry->d_inode->i_size;
if (fsize == 0 || fsize > ARRAY_SIZE(buf)) {
pr_err("%s invalid file(%s) size %d\n",
__func__, filename, fsize);
ret = -EEXIST;
goto exit;
}
memset(buf, 0, sizeof(buf));
nread = vfs_read(filp, (char __user *)buf, fsize, &filp->f_pos);
if (nread != fsize) {
pr_err("%s failed to read (ret %d)\n", __func__, nread);
ret = -EIO;
goto exit;
}
rc = sscanf(buf, "%d %d %d", &value[0], &value[1], &value[2]);
if (rc != 3) {
pr_err("%s failed to kstrtoint %d\n", __func__, rc);
ret = -EINVAL;
goto exit;
}
pr_info("%s %s(size %d) : %d %d %d\n",
__func__, filename, fsize, value[0], value[1], value[2]);
exit:
filp_close(filp, current->files);
set_fs(old_fs);
return ret;
}
static int dpui_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct mdnie_info *mdnie;
struct panel_device *panel;
struct panel_info *panel_data;
char tbuf[MAX_DPUI_VAL_LEN];
u8 coordinate[PANEL_COORD_LEN] = { 0, };
int def_wrgb_ofs_org[3] = { 0, };
int size;
mdnie = container_of(self, struct mdnie_info, dpui_notif);
panel = container_of(mdnie, struct panel_device, mdnie);
panel_data = &panel->panel_data;
mutex_lock(&mdnie->lock);
resource_copy_by_name(panel_data, coordinate, "coordinate");
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d",
(coordinate[0] << 8) | coordinate[1]);
set_dpui_field(DPUI_KEY_WCRD_X, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d",
(coordinate[2] << 8) | coordinate[3]);
set_dpui_field(DPUI_KEY_WCRD_Y, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d",
mdnie->props.def_wrgb_ofs[0]);
set_dpui_field(DPUI_KEY_WOFS_R, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d",
mdnie->props.def_wrgb_ofs[1]);
set_dpui_field(DPUI_KEY_WOFS_G, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d",
mdnie->props.def_wrgb_ofs[2]);
set_dpui_field(DPUI_KEY_WOFS_B, tbuf, size);
mdnie_get_efs(MDNIE_WOFS_ORG_PATH, def_wrgb_ofs_org);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", def_wrgb_ofs_org[0]);
set_dpui_field(DPUI_KEY_WOFS_R_ORG, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", def_wrgb_ofs_org[1]);
set_dpui_field(DPUI_KEY_WOFS_G_ORG, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", def_wrgb_ofs_org[2]);
set_dpui_field(DPUI_KEY_WOFS_B_ORG, tbuf, size);
mutex_unlock(&mdnie->lock);
return 0;
}
static int mdnie_register_dpui(struct mdnie_info *mdnie)
{
memset(&mdnie->dpui_notif, 0, sizeof(mdnie->dpui_notif));
mdnie->dpui_notif.notifier_call = dpui_notifier_callback;
return dpui_logging_register(&mdnie->dpui_notif, DPUI_TYPE_PANEL);
}
#endif /* CONFIG_DISPLAY_USE_INFO */
int mdnie_probe(struct panel_device *panel, struct mdnie_tune *mdnie_tune)
{
struct mdnie_info *mdnie = &panel->mdnie;
size_t i;
int ret = 0;
if (unlikely(!mdnie_tune)) {
pr_err("%s, mdnie_tune not exist\n", __func__);
return -ENODEV;
}
mdnie->class = class_create(THIS_MODULE, "mdnie");
if (IS_ERR_OR_NULL(mdnie->class)) {
pr_err("failed to create mdnie class\n");
ret = -EINVAL;
goto error0;
}
mdnie->dev = device_create(mdnie->class,
&panel->lcd->dev, 0, &mdnie, "mdnie");
if (IS_ERR_OR_NULL(mdnie->dev)) {
pr_err("failed to create mdnie device\n");
ret = -EINVAL;
goto error1;
}
dev_set_drvdata(mdnie->dev, mdnie);
mutex_lock(&mdnie->lock);
mdnie->props.enable = 0;
mdnie->props.scenario = UI_MODE;
mdnie->props.mode = AUTO;
mdnie->props.tuning = 0;
mdnie->props.bypass = BYPASS_OFF;
mdnie->props.hdr = HDR_OFF;
mdnie->props.light_notification = LIGHT_NOTIFICATION_OFF;
mdnie->props.hmd = HMD_MDNIE_OFF;
mdnie->props.night = NIGHT_MODE_OFF;
mdnie->props.night_level = NIGHT_LEVEL_6500K;
mdnie->props.color_lens = COLOR_LENS_OFF;
mdnie->props.color_lens_color = COLOR_LENS_COLOR_BLUE;
mdnie->props.color_lens_level = COLOR_LENS_LEVEL_20P;
mdnie->props.accessibility = ACCESSIBILITY_OFF;
mdnie->props.ldu = LDU_MODE_OFF;
mdnie->props.scr_white_mode = SCR_WHITE_MODE_NONE;
mdnie->props.trans_mode = TRANS_ON;
mdnie->props.update_sensorRGB = false;
mdnie->props.sz_scr = 0;
mdnie->props.num_ldu_mode = mdnie_tune->num_ldu_mode;
mdnie->props.num_night_level = mdnie_tune->num_night_level;
mdnie->props.num_color_lens_color = mdnie_tune->num_color_lens_color;
mdnie->props.num_color_lens_level = mdnie_tune->num_color_lens_level;
memcpy(mdnie->props.line, mdnie_tune->line, sizeof(mdnie->props.line));
memcpy(mdnie->props.coef, mdnie_tune->coef, sizeof(mdnie->props.coef));
memcpy(mdnie->props.vtx, mdnie_tune->vtx, sizeof(mdnie->props.vtx));
mdnie->props.cal_x_center = mdnie_tune->cal_x_center;
mdnie->props.cal_y_center = mdnie_tune->cal_y_center;
mdnie->props.cal_boundary_center = mdnie_tune->cal_boundary_center;
mdnie->seqtbl = mdnie_tune->seqtbl;
mdnie->nr_seqtbl = mdnie_tune->nr_seqtbl;
mdnie->etc_maptbl = mdnie_tune->etc_maptbl;
mdnie->nr_etc_maptbl = mdnie_tune->nr_etc_maptbl;
mdnie->maptbl = mdnie_tune->maptbl;
mdnie->nr_maptbl = mdnie_tune->nr_maptbl;
mdnie->scr_white_maptbl = mdnie_tune->scr_white_maptbl;
mdnie->nr_scr_white_maptbl = mdnie_tune->nr_scr_white_maptbl;
#ifdef CONFIG_SUPPORT_AFC
mdnie->afc_maptbl = mdnie_tune->afc_maptbl;
mdnie->nr_afc_maptbl = mdnie_tune->nr_afc_maptbl;
#endif
if (mdnie->nr_maptbl % MAX_MDNIE_MAPTBL) {
pr_err("%s invalid size of maptbl %d\n",
__func__, mdnie->nr_maptbl);
ret = -EINVAL;
goto error2;
}
mdnie->nr_reg = mdnie->nr_maptbl / MAX_MDNIE_MAPTBL;
mdnie_coordinate_tune(mdnie);
for (i = 0; i < mdnie->nr_etc_maptbl; i++) {
mdnie->etc_maptbl[i].pdata = mdnie;
maptbl_init(&mdnie->etc_maptbl[i]);
}
for (i = 0; i < mdnie->nr_maptbl; i++) {
mdnie->maptbl[i].pdata = mdnie;
maptbl_init(&mdnie->maptbl[i]);
}
for (i = 0; i < mdnie->nr_scr_white_maptbl; i++) {
mdnie->scr_white_maptbl[i].pdata = mdnie;
maptbl_init(&mdnie->scr_white_maptbl[i]);
}
#ifdef CONFIG_SUPPORT_AFC
for (i = 0; i < mdnie->nr_afc_maptbl; i++) {
mdnie->afc_maptbl[i].pdata = mdnie;
maptbl_init(&mdnie->afc_maptbl[i]);
}
#endif
for (i = 0; i < ARRAY_SIZE(mdnie_dev_attrs); i++) {
ret = device_create_file(mdnie->dev, &mdnie_dev_attrs[i]);
if (ret < 0) {
dev_err(mdnie->dev, "%s: failed to add %s sysfs entries, %d\n",
__func__, mdnie_dev_attrs[i].attr.name, ret);
goto error2;
}
}
mdnie_register_fb(mdnie);
#ifdef CONFIG_DISPLAY_USE_INFO
mdnie_register_dpui(mdnie);
#endif
mutex_unlock(&mdnie->lock);
mdnie_enable(mdnie);
dev_info(mdnie->dev, "registered successfully\n");
return 0;
error2:
mutex_unlock(&mdnie->lock);
error1:
class_destroy(mdnie->class);
error0:
pr_err("%s failed to probe mdnie\n", __func__);
return ret;
}
static int attr_find_and_store(struct device *dev,
const char *name, const char *buf, size_t size)
{
struct device_attribute *dev_attr;
struct kernfs_node *kn;
struct attribute *attr;
kn = kernfs_find_and_get(dev->kobj.sd, name);
if (!kn) {
pr_info("%s: not found: %s\n", __func__, name);
return 0;
}
attr = kn->priv;
dev_attr = container_of(attr, struct device_attribute, attr);
if (dev_attr && dev_attr->store)
dev_attr->store(dev, dev_attr, buf, size);
kernfs_put(kn);
return 0;
}
ssize_t attr_store_for_each(struct class *cls,
const char *name, const char *buf, size_t size)
{
struct class_dev_iter iter;
struct device *dev;
int error = 0;
struct class *class = cls;
if (!class)
return -EINVAL;
if (!class->p) {
WARN(1, "%s called for class '%s' before it was initialized",
__func__, class->name);
return -EINVAL;
}
class_dev_iter_init(&iter, class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
error = attr_find_and_store(dev, name, buf, size);
if (error)
break;
}
class_dev_iter_exit(&iter);
return error;
}