1900 lines
49 KiB
C
1900 lines
49 KiB
C
|
// 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_kunit.h"
|
||
|
#include "panel_drv.h"
|
||
|
#include "mdnie.h"
|
||
|
#include "panel_debug.h"
|
||
|
#ifdef CONFIG_EXYNOS_DECON_LCD_COPR
|
||
|
#include "copr.h"
|
||
|
#endif
|
||
|
#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_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;
|
||
|
else if (IS_HDR_MODE(mdnie))
|
||
|
mdnie_mode = MDNIE_HDR_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)) {
|
||
|
panel_dbg("block mdnie (%s->%s) in doze mode\n",
|
||
|
mdnie_mode_name[mdnie_mode],
|
||
|
mdnie_mode_name[MDNIE_BYPASS_MODE]);
|
||
|
mdnie_mode = MDNIE_BYPASS_MODE;
|
||
|
}
|
||
|
|
||
|
return mdnie_mode;
|
||
|
}
|
||
|
|
||
|
__mockable 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;
|
||
|
panel_err("unknown mdnie\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (index >= 0 && index < MAX_MDNIE_MAPTBL) {
|
||
|
panel_dbg("mdnie %s(%d), maptbl %s(%d) found\n",
|
||
|
mdnie_mode_name[mdnie_mode],
|
||
|
mdnie_mode, mdnie_maptbl_name[index], index);
|
||
|
} else {
|
||
|
panel_err("mdnie %s(%d), maptbl not found!! (%d)\n",
|
||
|
mdnie_mode_name[mdnie_mode],
|
||
|
mdnie_mode, index);
|
||
|
index = MDNIE_UI_MAPTBL;
|
||
|
}
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_get_maptbl_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)) {
|
||
|
panel_err("failed to find maptbl %d\n", index);
|
||
|
return NULL;
|
||
|
}
|
||
|
return &mdnie->maptbl[index];
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_find_maptbl);
|
||
|
|
||
|
struct maptbl *mdnie_find_etc_maptbl(struct mdnie_info *mdnie, int index)
|
||
|
{
|
||
|
if (unlikely(index < 0 || index >= mdnie->nr_etc_maptbl)) {
|
||
|
panel_err("failed to find maptbl %d\n", index);
|
||
|
return NULL;
|
||
|
}
|
||
|
return &mdnie->etc_maptbl[index];
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_find_etc_maptbl);
|
||
|
|
||
|
struct maptbl *mdnie_find_scr_white_maptbl(struct mdnie_info *mdnie, int index)
|
||
|
{
|
||
|
if (unlikely(index < 0 || index >= mdnie->nr_scr_white_maptbl)) {
|
||
|
panel_err("failed to find maptbl %d\n", index);
|
||
|
return NULL;
|
||
|
}
|
||
|
return &mdnie->scr_white_maptbl[index];
|
||
|
}
|
||
|
|
||
|
__visible_for_testing 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) {
|
||
|
panel_err("invalid parameter\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = resource_copy_by_name(panel_data, coordinate, "coordinate");
|
||
|
if (ret < 0) {
|
||
|
panel_err("failed to copy 'coordinate' resource\n");
|
||
|
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)
|
||
|
panel_warn("need to check coord_x:%d coord_y:%d)\n", *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]);
|
||
|
panel_dbg("coord %d %d area Q%d, result %lld %lld\n",
|
||
|
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]);
|
||
|
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_init_coordinate_tune(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int x, y;
|
||
|
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
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);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
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 is null\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!IS_PANEL_ACTIVE(panel))
|
||
|
return -EAGAIN;
|
||
|
|
||
|
#ifdef CONFIG_SUPPORT_AFC
|
||
|
panel_info("do mdnie-seq (mode:%s, afc:%s)\n",
|
||
|
mdnie_mode_name[mdnie_mode],
|
||
|
!mdnie->props.afc_on ? "off" : "on");
|
||
|
#else
|
||
|
panel_info("do mdnie-seq (mode:%s)\n",
|
||
|
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))
|
||
|
panel_err("failed to write mdnie seqtbl\n");
|
||
|
|
||
|
#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))
|
||
|
panel_err("failed to write afc seqtbl\n");
|
||
|
#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) {
|
||
|
panel_err("out of range index %d\n", 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) {
|
||
|
panel_err("out of range index %d\n", 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;
|
||
|
}
|
||
|
|
||
|
panel_dbg("scr_white_mode %s\n",
|
||
|
scr_white_mode_name[mdnie->props.scr_white_mode]);
|
||
|
}
|
||
|
|
||
|
int mdnie_set_def_wrgb(struct mdnie_info *mdnie,
|
||
|
unsigned char r, unsigned char g, unsigned char b)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mdnie->props.def_wrgb[RED] = r;
|
||
|
mdnie->props.def_wrgb[GREEN] = g;
|
||
|
mdnie->props.def_wrgb[BLUE] = b;
|
||
|
|
||
|
panel_dbg("def_wrgb: %d(%02X) %d(%02X) %d(%02X)\n",
|
||
|
mdnie->props.def_wrgb[RED], mdnie->props.def_wrgb[RED],
|
||
|
mdnie->props.def_wrgb[GREEN], mdnie->props.def_wrgb[GREEN],
|
||
|
mdnie->props.def_wrgb[BLUE], mdnie->props.def_wrgb[BLUE]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_set_def_wrgb);
|
||
|
|
||
|
int mdnie_set_cur_wrgb(struct mdnie_info *mdnie,
|
||
|
unsigned char r, unsigned char g, unsigned char b)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mdnie->props.cur_wrgb[RED] = r;
|
||
|
mdnie->props.cur_wrgb[GREEN] = g;
|
||
|
mdnie->props.cur_wrgb[BLUE] = b;
|
||
|
|
||
|
panel_dbg("cur_wrgb: %d(%02X) %d(%02X) %d(%02X)\n",
|
||
|
mdnie->props.cur_wrgb[RED], mdnie->props.cur_wrgb[RED],
|
||
|
mdnie->props.cur_wrgb[GREEN], mdnie->props.cur_wrgb[GREEN],
|
||
|
mdnie->props.cur_wrgb[BLUE], mdnie->props.cur_wrgb[BLUE]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_set_cur_wrgb);
|
||
|
|
||
|
int mdnie_cur_wrgb_to_byte_array(struct mdnie_info *mdnie,
|
||
|
unsigned char *dst, unsigned int stride)
|
||
|
{
|
||
|
if (!mdnie || !dst || !stride)
|
||
|
return -EINVAL;
|
||
|
|
||
|
copy_to_sliced_byte_array(dst,
|
||
|
mdnie->props.cur_wrgb, 0, MAX_COLOR * stride, stride);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_cur_wrgb_to_byte_array);
|
||
|
|
||
|
int mdnie_update_wrgb(struct mdnie_info *mdnie,
|
||
|
unsigned char r, unsigned char g, unsigned char b)
|
||
|
{
|
||
|
unsigned char src[MAX_COLOR] = { r, g, b };
|
||
|
unsigned char dst[MAX_COLOR] = { 0, };
|
||
|
int i, value;
|
||
|
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (mdnie->props.scr_white_mode < 0 ||
|
||
|
mdnie->props.scr_white_mode >= MAX_SCR_WHITE_MODE) {
|
||
|
panel_warn("out of range %d\n",
|
||
|
mdnie->props.scr_white_mode);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (mdnie->props.scr_white_mode == SCR_WHITE_MODE_COLOR_COORDINATE) {
|
||
|
mdnie_set_def_wrgb(mdnie, r, g, b);
|
||
|
for_each_color(i) {
|
||
|
value = (int)mdnie->props.def_wrgb[i] +
|
||
|
(int)((mdnie->props.mode == AUTO) ?
|
||
|
mdnie->props.def_wrgb_ofs[i] : 0);
|
||
|
dst[i] = min(max(value, 0), 255);
|
||
|
}
|
||
|
mdnie_set_cur_wrgb(mdnie, dst[RED], dst[GREEN], dst[BLUE]);
|
||
|
} else if (mdnie->props.scr_white_mode == SCR_WHITE_MODE_ADJUST_LDU) {
|
||
|
for_each_color(i) {
|
||
|
value = (int)src[i] + (int)(((mdnie->props.mode == AUTO) &&
|
||
|
(mdnie->props.scenario != EBOOK_MODE)) ?
|
||
|
mdnie->props.def_wrgb_ofs[i] : 0);
|
||
|
dst[i] = min(max(value, 0), 255);
|
||
|
}
|
||
|
mdnie_set_cur_wrgb(mdnie, dst[RED], dst[GREEN], dst[BLUE]);
|
||
|
} else if (mdnie->props.scr_white_mode == SCR_WHITE_MODE_SENSOR_RGB) {
|
||
|
mdnie_set_cur_wrgb(mdnie, r, g, b);
|
||
|
} else {
|
||
|
panel_warn("wrgb is not updated in scr_white_mode(%d)\n",
|
||
|
mdnie->props.scr_white_mode);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mdnie_update_wrgb);
|
||
|
|
||
|
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)) {
|
||
|
panel_info("mdnie is off state\n");
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (mdnie_coordinate_changed(mdnie)) {
|
||
|
mdnie_init_coordinate_tune(mdnie);
|
||
|
scr_white_maptbls_init(mdnie);
|
||
|
}
|
||
|
mdnie_update_scr_white_mode(mdnie);
|
||
|
|
||
|
ret = panel_set_mdnie(panel);
|
||
|
if (ret < 0 && ret != -EAGAIN) {
|
||
|
panel_err("failed to set mdnie %d\n", 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, WAKE_TIMEOUT_MSEC);
|
||
|
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) {
|
||
|
panel_err("invalid value=%d\n", value);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value=%d\n", 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)) {
|
||
|
panel_err("invalid scenario %d\n", value);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value=%d\n", 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))) {
|
||
|
panel_err("invalid size %d\n", ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (value < 0 || value >= ACCESSIBILITY_MAX) {
|
||
|
panel_err("unknown accessibility %d\n", value);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value: %d, cnt: %d\n", 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);
|
||
|
|
||
|
panel_info("%s\n", 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) {
|
||
|
panel_err("invalid value=%d\n", value);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value=%d\n", 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;
|
||
|
int hbm_ce_lux = (mdnie->props.hbm_ce_lux > 0) ?
|
||
|
mdnie->props.hbm_ce_lux : 40000;
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &value);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
mutex_lock(&mdnie->lock);
|
||
|
mdnie->props.hbm = (value < hbm_ce_lux) ? 0 : 1;
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
panel_info("hbm:%d (lux:%d hbm_ce_lux:%d)\n",
|
||
|
mdnie->props.hbm, value, hbm_ce_lux);
|
||
|
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)) {
|
||
|
panel_err("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, "%u %u %u",
|
||
|
&white_red, &white_green, &white_blue);
|
||
|
if (ret != 3)
|
||
|
return -EINVAL;
|
||
|
|
||
|
panel_info("white_r %u, white_g %u, white_b %u\n",
|
||
|
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)) {
|
||
|
panel_err("invalid offset %d %d %d\n",
|
||
|
wr_offset, wg_offset, wb_offset);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("wr_offset %d, wg_offset %d, wb_offset %d\n",
|
||
|
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;
|
||
|
|
||
|
panel_info("night_mode %s level %d\n",
|
||
|
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;
|
||
|
|
||
|
panel_info("color_lens_mode %s color %d level %d\n",
|
||
|
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) {
|
||
|
panel_err("invalid value=%d\n", value);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value=%d\n", 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;
|
||
|
|
||
|
panel_info("value=%d\n", 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) {
|
||
|
panel_err("out of range %d\n",
|
||
|
value);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value=%d\n", 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;
|
||
|
|
||
|
panel_info("value=%d\n", 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);
|
||
|
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) {
|
||
|
panel_err("invalid size %d\n", ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
panel_info("value=%d, cnt=%d\n", 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);
|
||
|
|
||
|
panel_info("%s\n", 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);
|
||
|
int ret;
|
||
|
|
||
|
if (IS_MDNIE_ENABLED(mdnie)) {
|
||
|
panel_info("mdnie already enabled\n");
|
||
|
panel_mdnie_update(panel);
|
||
|
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);
|
||
|
ret = panel_mdnie_update(panel);
|
||
|
if (ret < 0)
|
||
|
mdnie->props.enable = 0;
|
||
|
|
||
|
mutex_lock(&mdnie->lock);
|
||
|
mdnie->props.trans_mode = TRANS_ON;
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
|
||
|
panel_info("done %u\n", mdnie->props.enable);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mdnie_disable(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!IS_MDNIE_ENABLED(mdnie)) {
|
||
|
panel_info("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);
|
||
|
|
||
|
panel_info("done\n");
|
||
|
|
||
|
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;
|
||
|
|
||
|
panel_dbg("%d\n", 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;
|
||
|
fb_register_client(&mdnie->fb_notif);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mdnie_unregister_fb(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
fb_unregister_client(&mdnie->fb_notif);
|
||
|
mdnie->fb_notif.notifier_call = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_DISPLAY_USE_INFO
|
||
|
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 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);
|
||
|
|
||
|
#if 0 /* disable for GKI build */
|
||
|
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);
|
||
|
#endif
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
static int mdnie_unregister_dpui(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
dpui_logging_unregister(&mdnie->dpui_notif);
|
||
|
mdnie->dpui_notif.notifier_call = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* CONFIG_DISPLAY_USE_INFO */
|
||
|
|
||
|
__visible_for_testing int mdnie_init_property(struct mdnie_info *mdnie, struct mdnie_tune *mdnie_tune)
|
||
|
{
|
||
|
if (!mdnie || !mdnie_tune)
|
||
|
return -EINVAL;
|
||
|
|
||
|
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;
|
||
|
|
||
|
/* initialization by mdnie_tune */
|
||
|
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->props.hbm_ce_lux = mdnie_tune->hbm_ce_lux;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_init_tables(struct mdnie_info *mdnie, struct mdnie_tune *mdnie_tune)
|
||
|
{
|
||
|
if (!mdnie || !mdnie_tune)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (mdnie_tune->nr_maptbl % MAX_MDNIE_MAPTBL) {
|
||
|
panel_err("invalid size of maptbl %d\n", mdnie_tune->nr_maptbl);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
|
||
|
mdnie->nr_reg = mdnie->nr_maptbl / MAX_MDNIE_MAPTBL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_init_maptbls(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
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
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_set_name(struct mdnie_info *mdnie, unsigned int id)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (id == 0)
|
||
|
snprintf(mdnie->name, MAX_MDNIE_DEV_NAME_SIZE,
|
||
|
"%s", MDNIE_DEV_NAME);
|
||
|
else
|
||
|
snprintf(mdnie->name, MAX_MDNIE_DEV_NAME_SIZE,
|
||
|
"%s-%d", MDNIE_DEV_NAME, id);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing const char *mdnie_get_name(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
return mdnie ? mdnie->name : NULL;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_create_class(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mdnie->class = class_create(THIS_MODULE, mdnie_get_name(mdnie));
|
||
|
if (IS_ERR_OR_NULL(mdnie->class)) {
|
||
|
panel_err("failed to create mdnie class\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_destroy_class(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!mdnie || !mdnie->class)
|
||
|
return -EINVAL;
|
||
|
|
||
|
class_destroy(mdnie->class);
|
||
|
mdnie->class = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_create_device(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (!to_panel_device(mdnie)->lcd_dev) {
|
||
|
panel_err("lcd_device is null\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
mdnie->dev = device_create(mdnie->class,
|
||
|
to_panel_device(mdnie)->lcd_dev,
|
||
|
0, &mdnie, "%s", mdnie_get_name(mdnie));
|
||
|
if (IS_ERR_OR_NULL(mdnie->dev)) {
|
||
|
panel_err("failed to create mdnie device\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
dev_set_drvdata(mdnie->dev, mdnie);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_destroy_device(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (!mdnie->dev) {
|
||
|
panel_err("mdnie device is null\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
device_unregister(mdnie->dev);
|
||
|
mdnie->dev = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_create_device_files(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int i, ret;
|
||
|
|
||
|
if (!mdnie || !mdnie->dev)
|
||
|
return -EINVAL;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(mdnie_dev_attrs); i++) {
|
||
|
ret = device_create_file(mdnie->dev, &mdnie_dev_attrs[i]);
|
||
|
if (ret < 0) {
|
||
|
panel_err("failed to add %s sysfs entries, %d\n",
|
||
|
mdnie_dev_attrs[i].attr.name, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_remove_device_files(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (!mdnie || !mdnie->dev)
|
||
|
return -EINVAL;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(mdnie_dev_attrs); i++)
|
||
|
device_remove_file(mdnie->dev, &mdnie_dev_attrs[i]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_create_class_and_device(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mdnie_create_class(mdnie);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
ret = mdnie_create_device(mdnie);
|
||
|
if (ret < 0)
|
||
|
goto error1;
|
||
|
|
||
|
ret = mdnie_create_device_files(mdnie);
|
||
|
if (ret < 0)
|
||
|
goto error2;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error2:
|
||
|
mdnie_destroy_device(mdnie);
|
||
|
error1:
|
||
|
mdnie_destroy_class(mdnie);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
__visible_for_testing int mdnie_remove_class_and_device(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = mdnie_remove_device_files(mdnie);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mdnie_destroy_device(mdnie);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mdnie_destroy_class(mdnie);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mdnie_init(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mutex_init(&mdnie->lock);
|
||
|
|
||
|
ret = mdnie_set_name(mdnie,
|
||
|
to_panel_device(mdnie)->id);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mdnie_create_class_and_device(mdnie);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
panel_info("mdnie init success\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mdnie_exit(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mutex_lock(&mdnie->lock);
|
||
|
mdnie_remove_class_and_device(mdnie);
|
||
|
memset(mdnie->name, 0, sizeof(mdnie->name));
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mdnie_probe(struct mdnie_info *mdnie, struct mdnie_tune *mdnie_tune)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (unlikely(!mdnie || !mdnie_tune)) {
|
||
|
panel_err("invalid argument\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&mdnie->lock);
|
||
|
ret = mdnie_init_property(mdnie, mdnie_tune);
|
||
|
if (ret < 0)
|
||
|
goto err;
|
||
|
|
||
|
ret = mdnie_init_tables(mdnie, mdnie_tune);
|
||
|
if (ret < 0)
|
||
|
goto err;
|
||
|
|
||
|
ret = mdnie_init_coordinate_tune(mdnie);
|
||
|
if (ret < 0)
|
||
|
goto err;
|
||
|
|
||
|
ret = mdnie_init_maptbls(mdnie);
|
||
|
if (ret < 0)
|
||
|
goto err;
|
||
|
|
||
|
ret = mdnie_register_fb(mdnie);
|
||
|
if (ret < 0)
|
||
|
goto err;
|
||
|
|
||
|
#ifdef CONFIG_DISPLAY_USE_INFO
|
||
|
ret = mdnie_register_dpui(mdnie);
|
||
|
if (ret < 0)
|
||
|
goto err;
|
||
|
#endif
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
|
||
|
ret = mdnie_enable(mdnie);
|
||
|
if (ret < 0) {
|
||
|
panel_err("failed to enable mdnie\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
panel_info("mdnie probe success\n");
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err:
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
panel_err("failed to probe mdnie\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int mdnie_remove(struct mdnie_info *mdnie)
|
||
|
{
|
||
|
if (!mdnie)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mdnie_disable(mdnie);
|
||
|
mutex_lock(&mdnie->lock);
|
||
|
#ifdef CONFIG_DISPLAY_USE_INFO
|
||
|
mdnie_unregister_dpui(mdnie);
|
||
|
#endif
|
||
|
mdnie_unregister_fb(mdnie);
|
||
|
|
||
|
mutex_unlock(&mdnie->lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|